Compare commits

..

85 Commits

Author SHA1 Message Date
Zachary Yedidia
5044ccf6bb Update keybinding docs
Also updates the pane type of `info` to `command` which is a more
descriptive name.
2020-09-06 17:38:23 -04:00
Zachary Yedidia
4ac511b597 Update tcell 2020-09-06 11:40:34 -04:00
Zachary Yedidia
96601a915d Replace meta with alt automatically, update tcell 2020-09-05 21:59:19 -04:00
Zachary Yedidia
11104fd093 Update to tcell v2 2020-09-05 14:52:35 -04:00
Zachary Yedidia
f35f507832 Never backup closed buffers 2020-09-04 13:36:23 -04:00
Zachary Yedidia
04c1430747 Merge 2020-08-30 15:46:14 -04:00
Zachary Yedidia
548bb98641 Properly close unmodified buffers on sigterm 2020-08-30 15:46:11 -04:00
Zachary Yedidia
c9e49fa1a4 Update tcell 2020-08-25 15:21:18 -04:00
Dmitry Maluka
c9b0451a33 AddToHistory function for plugins (#1830)
Add InfoBuf's method AddToHistory function which adds a new item
to the history for the prompt type `ptype`.
This function is not used by micro itself. It is useful for plugins
which add their own items to the history, bypassing the infobar
command line.
2020-08-23 15:47:14 -04:00
Peder B. Sundt
5bee7272e9 Add improvements to the python3 syntax definitions (#1833)
* Python3: Add built-in object 'cls'

* Python3: Add the bitwise negation operator ~

* Python3: Add support for hexadeximal and binary numerical literals

* Python3: Rewrite '(__foo__|__bar__)' as '__(foo|bar)__', add known '__i.*__' methods

* Python3: Add __iter__ as a magic method, sort the list of magic methods

* Python3: Numerical literals: Add support for '_', disallow leading 0 for decimals

* fixup! Python3: Numerical literals: Add support for '_', disallow leading 0 for decimals

hex oct and bin have different sets of allowed digits

* Python3: Add support for floating point numbers with optional scientific notation

* Python3: stop single-line strings at EOL

* Python3: Add support for TODO and FIXME in comments

* Python3: Add support for the ^ bitwise xor operator
2020-08-23 15:46:27 -04:00
Peder B. Sundt
11291e1406 Minor tweak to the railscast color scheme (#1834)
* Railscast: Change the color of operators to match keywords instead of identifiers

* Railscast: Add a color for `constant.specialChar`
2020-08-23 15:46:05 -04:00
Dmitry Maluka
c7e72220dd Add scrollbar color group (#1840)
Ref #1837
2020-08-23 15:45:43 -04:00
Bartek Pacia
3ba03cca15 fix spelling (#1828) 2020-08-17 13:04:44 -04:00
Ryan Westlund
c6d04220be Highlight static as keyword in Javascript files (#1824) 2020-08-14 16:56:55 -04:00
Dmitry Maluka
7e19b68426 Avoid duplicate entries in history (#1822) 2020-08-13 01:38:50 -04:00
Zachary Yedidia
c5bafbc1c5 Merge 2020-08-12 01:18:18 -04:00
Zachary Yedidia
6b80870dfd Don't auto-relocate mouse events 2020-08-12 01:18:15 -04:00
Zachary Yedidia
5cb618c466 Improve showkey command 2020-08-11 22:18:10 -04:00
Ryan Westlund
a87370b111 Improve Rust syntax highlighting (#1820) 2020-08-11 21:39:57 -04:00
Zachary Yedidia
352f57cf11 Enable registering raw events
Fixes #1821
2020-08-11 14:36:58 -04:00
Zachary Yedidia
1e83e666fb Don't overwrite user bindings
This fix still needs more work.

Ref #1821
2020-08-11 01:43:41 -04:00
Zachary Yedidia
c837a7d0b7 Ref #1819 2020-08-10 20:34:10 -04:00
Zachary Yedidia
63d45bc9c5 Fix JobSend stdin 2020-08-10 12:24:29 -04:00
Zachary Yedidia
0283c01432 Record events in cursor 2020-08-09 16:42:03 -04:00
Zachary Yedidia
bbd6f559ab Allow configuration for info/term bindings
This commit exposes the separate infopane bindings to configuration
from the user. This also adds support for separate bindings in the
terminal emulator view. Default bindings are provided, but can also
be rebound in bindings.json.
2020-08-09 16:42:03 -04:00
Zachary Yedidia
2363a4019b Separate bindings for buffers and command bar
This commit separates actions in the command bar from actions in
a normal buffer, and implements what is needed to allow rebinding,
although an interface for command bar keybindings is not yet exposed
to the user.
2020-08-09 16:42:03 -04:00
Zachary Yedidia
d33c28eeb8 Preliminary support for key sequences
This commit adds support for binding key sequences such as
"<Ctrl-x><Ctrl-c>". This commit does not solve the problem
of global bindings yet, and therefore the command bar doesn't
work properly in this commit.
2020-08-09 16:42:03 -04:00
Zachary Yedidia
5ff8b3791d Basic implementation of KeyTree 2020-08-09 16:42:03 -04:00
Zachary Yedidia
6c53407e6d Improve internal keyevent names 2020-08-09 16:42:03 -04:00
Robin Voetter
626b08e991 Improve Zig syntax definitions (#1814)
* Improve Zig syntax definitions

* Remove duplicate definition of constant.number

* Make undefined a constant
2020-08-05 19:39:03 -04:00
Jakob Nybo Nissen
697751d5f6 Allow Julia multiline strings and comments (#1813) 2020-08-05 19:38:29 -04:00
Zachary Yedidia
dd54a64746 Initialize t.release to true 2020-08-04 18:41:14 -04:00
Dmitry Maluka
6e43af31cb Fix non-working split resize with mouse drag (#1811)
Fix the 2nd part of #1773: resize via mouse drag doesn't work if the
split on the left contains other splits, i.e. is not a leaf node.

The problem is that only leaf nodes have unique id. For non-leaf nodes
ID() returns 0. So we shouldn't search the node by id.
So replace GetMouseSplitID() with GetMouseSplitNode().
2020-08-04 18:37:19 -04:00
Dmitry Maluka
a4cc5a4146 Fix erased vertical dividing line (#1810)
Fix the 1st part of #1773: the dividing line between vertical splits
is not displayed if the split on the left contains other splits, i.e.
is not a leaf node.
2020-08-04 18:33:16 -04:00
Morten Linderud
9a3fb52b42 Support reproducible builds (#1802)
* Makefile: Ensure we strip out embedded paths

To reproduce binaries undeterministic values needs to be removed. By
default Go embeds several module paths into the binaries, which prevents
people from reproducing said distributed binary.

The distributed binary from micro contains the full home path of the
current builder of the binary. -trimpath removes these paths from the
binary.

    $ strings micro | grep "/home/zyedidia" | wc -l
    868

This also helps other distributions providing reproducible versions of
micro down the line.

Signed-off-by: Morten Linderud <morten@linderud.pw>

* build-date: Ensure build time adheres to SOURCE_DATE_EPOCH

Embedding undeterministic values into binaries prevents reproduction of
the binaries. The reproducible builds projects defines
`SOURCE_DATE_EPOCH` to allow deterministic insertion of build times.

This patch ensures `build-date` checks the environment variable before
building with the local time.

    $ SOURCE_DATE_EPOCH=123123 go run tools/build-date.go
    January 02, 1970
    $ go run tools/build-date.go
    July 31, 2020

    $ make build-quick && ./micro --version
    [...]
    Compiled on July 31, 2020
    $ SOURCE_DATE_EPOCH=123123 make build-quick && ./micro --version
    [...]
    Compiled on January 02, 1970

https://reproducible-builds.org/specs/source-date-epoch/

Signed-off-by: Morten Linderud <morten@linderud.pw>
2020-08-01 20:26:39 -04:00
Zachary Yedidia
cfb8d4a6b5 Merge 2020-08-01 20:18:41 -04:00
Zachary Yedidia
b507cd26f4 Exit gracefully when SIGTERM is received
Ref #779
2020-08-01 20:18:07 -04:00
Zachary Yedidia
ce46b8e9a1 Solus install instruction 2020-07-30 21:53:30 -04:00
Zachary Yedidia
95ec55fbbf Check error in terminal emulator 2020-07-27 17:43:55 -04:00
Zachary Yedidia
015e7c7b83 Don't update internal plugins
Ref #1792
2020-07-22 15:40:40 -04:00
Ryan Westlund
1f27f51f9a Add syntax support for Renpy (#1789) 2020-07-22 15:26:37 -04:00
Zachary Yedidia
ab1d74dc79 Tidy go mod 2020-07-22 15:23:13 -04:00
Zachary Yedidia
2b1ebd5fb7 Merge 2020-07-19 19:08:00 -04:00
Zachary Yedidia
b521e47d0b Update tcell 2020-07-19 19:07:52 -04:00
Ryan Westlund
1343955b05 Add comment support for OCaml (#1776) 2020-07-15 14:13:44 -04:00
franekjel
1a89d2095d Support for multiple modifiers in colorschemes (#1772)
* Support for multiple modifiers (eg. "bold italic")

* Test for multiple modifiers (bold + italic + underline)
2020-07-14 17:58:03 -04:00
Zachary Yedidia
781a2dd826 Add flake8 linter, postinit and preinit
Closes #1768
2020-07-13 13:28:26 -04:00
Zachary Yedidia
a45591a24d Read paste option in screen init
Fixes #1767
2020-07-10 12:26:15 -04:00
Zachary Yedidia
a52dbb2142 Fix swift linting problem 2020-07-09 18:08:14 -04:00
Zachary Yedidia
41a27cc58a Update linter to include eslint
Ref #1766
2020-07-09 18:04:40 -04:00
Zachary Yedidia
3d387732c4 Update linter documentation
Ref #1766
2020-07-09 17:58:42 -04:00
Zachary Yedidia
04f281bf1d Name svg micro.svg in tarballs
Ref #1765
2020-07-09 13:29:48 -04:00
Zachary Yedidia
2caff00ce1 Add micro icon and desktop file to release zips
Closes #1765
2020-07-09 13:26:12 -04:00
Ján Priner
ddc887f6e4 Highlight multicharacter escape sequences in C and C++ string literals (#1761) 2020-07-08 00:17:48 -04:00
Ján Priner
51444765f4 Update micro.desktop (#1759) 2020-07-06 17:55:13 -04:00
Zachary Yedidia
767918d2d1 fix 2020-07-06 17:32:41 +00:00
Zachary Yedidia
984b6d4e6a Rename deb file in scripts 2020-07-06 17:30:49 +00:00
Zachary Yedidia
4184bc19e0 Improve deb package 2020-07-06 17:25:30 +00:00
Zachary Yedidia
0d81e68f86 Chmod 2020-07-06 03:21:13 +00:00
Zachary Yedidia
8316bce6eb Update script 2020-07-06 03:19:47 +00:00
Zachary Yedidia
f1318f28ea Merge 2020-07-06 03:14:06 +00:00
Zachary Yedidia
0283155305 Improve packaging
Slight improvements to the man page, and the man page is now
provided in prebuilt binary tarballs. Also a .deb file is now
provided as an asset along with prebuilt binary tarballs.
2020-07-06 03:13:02 +00:00
Zachary Yedidia
cd0a9b6a60 Update nightly release scripts 2020-07-05 17:12:06 -04:00
Zachary Yedidia
621e4e9e4d Slight performance improvement 2020-07-05 15:54:00 -04:00
Zachary Yedidia
806525c3da Improve comment plugin
When commenting a selection, the plugin won't just toggle each
line individually but will only uncomment the block if it is all
comments.

The comment plugin also now takes into account any number of spaces
between the comment character and the text. For example '//comment' will
be uncommented properly, as well as '//      comment'.

Fixes #1758
2020-07-05 15:48:49 -04:00
Zachary Yedidia
102ae04a16 Improve multicursor clipboard
Ref #1721
2020-07-05 01:12:35 -04:00
Zachary Yedidia
037c3c993f Add clipboard support for multicursors
Fixes #1721
2020-07-04 21:26:36 -04:00
Zachary Yedidia
d8596919a6 Fix reading clipboard internally for OSC52 2020-07-04 20:54:27 -04:00
Zachary Yedidia
cf86f6848f Don't set fastdirty base if modified 2020-07-04 20:09:44 -04:00
Zachary Yedidia
aeb5563df0 Update runtime 2020-07-04 20:06:37 -04:00
Zachary Yedidia
a3eacb785f Merge 2020-07-04 20:01:02 -04:00
Zachary Yedidia
f143418267 Add support for copy-paste via OSC 52
Ref #1754
2020-07-04 20:00:39 -04:00
Jim Tittsler
9003462e76 Fix README ToC link (#1757) 2020-07-04 19:47:10 -04:00
Zachary Yedidia
67355337b3 Fix escape not exiting prompt 2020-07-03 22:12:58 -04:00
Zachary Yedidia
32c8517a90 Rebind escape to clear info and deselect 2020-07-03 21:02:16 -04:00
Zachary Yedidia
b793e9bb92 Use tcell's CanDisplay instead of Go's IsPrint
Fixes #1755
2020-07-02 17:57:50 -04:00
Zachary Yedidia
977290d77b Improve php string highlighting
Fixes #1753
2020-07-01 23:38:47 -04:00
Zachary Yedidia
2c4035aa65 Update clipboard verification
Ref #1752
2020-07-01 01:24:06 -04:00
Utkarsh Gupta
b748d0c383 Drop unnecessary Ftoa and FtoaWithDigits function (#1751)
Fixes: #1749

Signed-off-by: Utkarsh Gupta <utkarsh@debian.org>
2020-06-30 13:19:27 -04:00
Zachary Yedidia
b8fbbf5c83 Only lock event handling 2020-06-28 16:34:01 -04:00
Zachary Yedidia
253281ae5e Add a lock for plugins to use if using async code
Ref #1539
2020-06-28 16:29:32 -04:00
Zachary Yedidia
f5c6f66c8f Fix path escaping on Windows
Windows does not allow ':' in a path, but for some reason previous
versions still worked, except the file for storing buffer info
(which had a ':' in the name) was not viewable except by opening
it with micro.

Ref #1736
2020-06-27 17:59:28 -04:00
Zachary Yedidia
3da0415ef1 Merge 2020-06-27 17:55:39 -04:00
Zachary Yedidia
be4d186a46 Close file properly in clean and update makefile 2020-06-27 17:55:01 -04:00
Ryan Westlund
e946d9eddf Improve Haskell syntax highlighting (#1745) 2020-06-26 17:53:43 -04:00
76 changed files with 2283 additions and 661 deletions

View File

@@ -6,7 +6,8 @@ HASH = $(shell git rev-parse --short HEAD)
DATE = $(shell GOOS=$(shell go env GOHOSTOS) GOARCH=$(shell go env GOHOSTARCH) \
go run tools/build-date.go)
ADDITIONAL_GO_LINKER_FLAGS = $(shell GOOS=$(shell go env GOHOSTOS) \
GOARCH=$(shell go env GOHOSTARCH))
GOARCH=$(shell go env GOHOSTARCH) \
go run tools/info-plist.go "$(VERSION)")
GOBIN ?= $(shell go env GOPATH)/bin
GOVARS = -X github.com/zyedidia/micro/v2/internal/util.Version=$(VERSION) -X github.com/zyedidia/micro/v2/internal/util.CommitHash=$(HASH) -X 'github.com/zyedidia/micro/v2/internal/util.CompileDate=$(DATE)'
DEBUGVAR = -X github.com/zyedidia/micro/v2/internal/util.Debug=ON
@@ -14,20 +15,20 @@ VSCODE_TESTS_BASE_URL = 'https://raw.githubusercontent.com/microsoft/vscode/e6a4
# Builds micro after checking dependencies but without updating the runtime
build:
go build -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
go build -trimpath -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
build-dbg:
go build -ldflags "-s -w $(ADDITIONAL_GO_LINKER_FLAGS) $(DEBUGVAR)" ./cmd/micro
go build -trimpath -ldflags "-s -w $(ADDITIONAL_GO_LINKER_FLAGS) $(DEBUGVAR)" ./cmd/micro
build-tags: fetch-tags
go build -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
go build -trimpath -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
# Builds micro after building the runtime and checking dependencies
build-all: runtime build
# Builds micro without checking for dependencies
build-quick:
go build -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
go build -trimpath -ldflags "-s -w $(GOVARS) $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
# Same as 'build' but installs to $GOBIN afterward
install:

View File

@@ -32,7 +32,7 @@ You can also check out the website for Micro at https://micro-editor.github.io.
- [macOS terminal](#macos-terminal)
- [Linux clipboard support](#linux-clipboard-support)
- [Colors and syntax highlighting](#colors-and-syntax-highlighting)
- [Plan9, Cygwin, Mingw](#plan9-cygwin-mingw)
- [Cygwin, Mingw, Plan9](#cygwin-mingw-plan9)
- [Usage](#usage)
- [Documentation and Help](#documentation-and-help)
- [Contributing](#contributing)
@@ -130,6 +130,7 @@ for other operating systems. These packages are not guaranteed to be up-to-date.
* `apt install micro` (Ubuntu 20.04 `focal`, and Debian `unstable | testing | buster-backports`). At the moment, this package (2.0.1-1) is outdated and has a known bug where debug mode is enabled.
* `dnf install micro` (Fedora).
* `yay -S micro` (Arch Linux).
* `eopkg install micro` (Solus).
* See [wiki](https://github.com/zyedidia/micro/wiki/Installing-Micro) for details about CRUX, Termux.
* Windows: [Chocolatey](https://chocolatey.org) and [Scoop](https://github.com/lukesampson/scoop).
* `choco install micro`.

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 103.2 103.2"
enable-background="new 0 0 960 560"
xml:space="preserve"
inkscape:version="0.91 r13725"
sodipodi:docname="micro-logo-notext.svg"
width="103.2"
height="103.2"><metadata
id="metadata9"><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="defs7" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="733"
inkscape:window-height="480"
id="namedview5"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="0.28541667"
inkscape:cx="302"
inkscape:cy="-4"
inkscape:window-x="1699"
inkscape:window-y="277"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" /><path
d="M 51.6,0 C 23.1,0 0,23.1 0,51.6 c 0,28.5 23.1,51.6 51.6,51.6 28.5,0 51.6,-23.1 51.6,-51.6 C 103.2,23.1 80.1,0 51.6,0 Z m 24.5,58.6 c -0.5,2 -1.3,3.6 -2.4,4.9 -1,1.3 -2,2.1 -3.1,2.5 -1.1,0.4 -2.2,0.6 -3.4,0.6 -1.2,0 -2.2,-0.2 -3,-0.7 C 63.4,65.5 62.8,64.8 62.3,64 61.8,63.2 61.5,62.2 61.3,61.1 61.1,60 61,58.8 61,57.5 c 0,-0.5 0,-1 0.1,-1.7 0.1,-0.7 0.2,-1.6 0.3,-1.6 l -0.2,0 c -1.6,4 -3.8,6.9 -6.6,9.2 -2.8,2.3 -5.9,3.4 -9.3,3.4 -2.3,0 -4.2,-0.9 -5.5,-2.6 -1.4,-1.7 -2.1,-4.3 -2.1,-7.7 0,-0.5 0,-1 0.1,-1.6 0.1,-0.5 0.1,-0.7 0.2,-1.7 l -0.7,0 c -0.9,2 -1.7,4.8 -2.3,7.3 -0.6,2.5 -1.1,4.8 -1.4,6.9 -0.4,2.1 -0.6,4 -0.8,5.6 -0.2,1.6 -0.3,2.7 -0.4,3.3 0.1,0.5 0.2,1 0.3,1.6 0.2,0.6 0.3,1.2 0.5,1.7 0.2,0.5 0.3,1.1 0.4,1.6 0.1,0.5 0.2,0.9 0.2,1.2 0,1.4 -0.3,2.5 -0.9,3.2 -0.6,0.7 -1.3,1.1 -2,1.1 -0.9,0 -1.7,-0.3 -2.3,-0.8 -0.7,-0.6 -1,-1.5 -1,-2.7 0,-1.7 0.3,-3.9 0.9,-6.5 0.6,-2.6 1.5,-5.9 2.6,-9.8 0.6,-1.8 1.1,-3.6 1.7,-5.4 0.6,-1.8 1.1,-3.5 1.6,-5 0.5,-1.5 0.9,-2.9 1.3,-4.1 0.4,-1.2 0.6,-2.1 0.7,-2.8 0.1,-0.3 0.2,-1 0.3,-2 0.1,-1 0.2,-2.1 0.4,-3.4 0.2,-1.3 0.3,-2.7 0.5,-4.1 0.2,-1.5 0.4,-2.8 0.5,-4 0.2,-0.9 0.3,-1.9 0.5,-3 0.2,-1.1 0.5,-2.2 0.9,-3.1 0.4,-1 1,-1.8 1.7,-2.5 0.7,-0.7 1.6,-1 2.7,-1 1.2,0 2,0.4 2.4,1.1 0.4,0.7 0.6,1.6 0.5,2.6 -0.1,1 -0.2,2.1 -0.5,3.2 -0.3,1.1 -0.6,2.1 -0.9,2.9 -0.8,2.5 -1.6,4.8 -2.5,6.7 -0.9,1.9 -1.7,4 -2.4,6.2 -0.6,1.5 -0.8,2.9 -0.8,4.1 0,2.2 0.7,3.8 2,5 1.4,1.2 3,1.7 4.9,1.7 1.5,0 3,-0.5 4.4,-1.6 1.4,-1.1 2.7,-2.4 3.9,-3.9 1.2,-1.5 2.2,-3.1 3,-4.9 0.8,-1.7 1.4,-3.3 1.8,-4.6 0.1,-0.2 0.2,-0.6 0.3,-1.4 0.2,-0.8 0.3,-1.7 0.5,-2.7 0.2,-1 0.4,-2 0.6,-3.1 0.2,-1.1 0.4,-2 0.5,-2.7 0.2,-0.8 0.3,-1.6 0.5,-2.6 0.2,-1 0.5,-1.9 0.9,-2.8 0.4,-0.9 1,-1.6 1.6,-2.2 0.7,-0.6 1.5,-0.9 2.6,-0.9 1.3,0 2.1,0.4 2.6,1.1 0.4,0.7 0.6,1.6 0.6,2.6 -0.1,1 -0.2,2 -0.5,3 -0.3,1 -0.5,1.8 -0.7,2.4 -0.8,2.5 -1.6,4.7 -2.4,6.7 -0.8,2 -1.5,3.8 -2.2,5.2 -0.6,1.5 -1.1,2.6 -1.5,3.5 -0.4,0.9 -0.6,1.5 -0.6,1.8 0,2.6 0.6,4.5 1.7,5.6 1.1,1.1 2.3,1.7 3.6,1.7 2.2,0 3.9,-0.7 5.2,-2 1.3,-1.4 2.3,-3.9 2.9,-6.9 l 0.8,0 c 0.2,2.9 -0.1,5.3 -0.6,7.3 z"
id="path3"
inkscape:connector-curvature="0"
style="fill:#2e3192" /></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -14,6 +14,8 @@ of modern terminals. It comes as one single, batteries-included, static binary w
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).
Use Ctrl-q to quit, Ctrl-s to save, and Ctrl-g to open the in-editor help menu.
.SH OPTIONS
.PP
\-clean
@@ -51,7 +53,7 @@ Enable debug mode (enables logging to ./log.txt)
Show the version number and information
.RE
Micro's plugin's can be managed at the command line with the following commands.
Micro's plugins can be managed at the command line with the following commands.
.RS 4
.PP
@@ -119,5 +121,5 @@ and to report any newly encountered bugs you may find. We strive to correct
bugs as swiftly as possible.
.SH COPYRIGHT
Copyright \(co 2020 Zachary Yedidia, et al.
See /usr/share/doc/micro/LICENSE and /usr/share/doc/micro/AUTHORS for more information.
Copyright \(co 2020 Zachary Yedidia, et al. MIT license.
See \fBhttps://github.com/zyedidia/micro\fP for details.

View File

@@ -6,10 +6,10 @@ Comment=Edit text files in a terminal
Icon=micro
Type=Application
Categories=terminal;TextEditor;
Categories=Utility;TextEditor;Development;
Keywords=text;editor;syntax;terminal;
Exec=micro %U
Exec=micro %F
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

@@ -97,14 +97,13 @@ func CleanConfig() {
file, e := os.Open(fname)
if e == nil {
defer file.Close()
decoder := gob.NewDecoder(file)
err = decoder.Decode(&buffer)
if err != nil && f.Name() != "history" {
badFiles = append(badFiles, fname)
}
file.Close()
}
}

View File

@@ -56,6 +56,7 @@ func luaImportMicro() *lua.LTable {
ulua.L.SetField(pkg, "Tabs", luar.New(ulua.L, func() *action.TabList {
return action.Tabs
}))
ulua.L.SetField(pkg, "Lock", luar.New(ulua.L, ulua.Lock))
return pkg
}

View File

@@ -5,10 +5,12 @@ import (
"fmt"
"io/ioutil"
"os"
"os/signal"
"regexp"
"runtime"
"sort"
"strconv"
"syscall"
"time"
"github.com/go-errors/errors"
@@ -16,16 +18,17 @@ import (
lua "github.com/yuin/gopher-lua"
"github.com/zyedidia/micro/v2/internal/action"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/clipboard"
"github.com/zyedidia/micro/v2/internal/config"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
var (
// Event channel
events chan tcell.Event
autosave chan bool
// Command line flags
@@ -268,16 +271,41 @@ func main() {
os.Exit(1)
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Kill, syscall.SIGTERM)
go func() {
<-c
for _, b := range buffer.OpenBuffers {
if !b.Modified() {
b.Fini()
}
}
if screen.Screen != nil {
screen.Screen.Fini()
}
os.Exit(0)
}()
m := clipboard.SetMethod(config.GetGlobalOption("clipboard").(string))
clipErr := clipboard.Initialize(m)
defer func() {
if err := recover(); err != nil {
screen.Screen.Fini()
fmt.Println("Micro encountered an error:", err)
if screen.Screen != nil {
screen.Screen.Fini()
}
if e, ok := err.(*lua.ApiError); ok {
fmt.Println("Lua API error:", e)
} else {
fmt.Println("Micro encountered an error:", errors.Wrap(err, 2).ErrorStack(), "\nIf you can reproduce this error, please report it at https://github.com/zyedidia/micro/issues")
}
// backup all open buffers
for _, b := range buffer.OpenBuffers {
b.Backup()
}
// Print the stack trace too
fmt.Print(errors.Wrap(err, 2).ErrorStack())
os.Exit(1)
}
}()
@@ -295,6 +323,11 @@ func main() {
screen.TermMessage(err)
}
err = config.RunPluginFn("preinit")
if err != nil {
screen.TermMessage(err)
}
args := flag.Args()
b := LoadInput(args)
@@ -312,7 +345,16 @@ func main() {
screen.TermMessage(err)
}
events = make(chan tcell.Event)
err = config.RunPluginFn("postinit")
if err != nil {
screen.TermMessage(err)
}
if clipErr != nil {
action.InfoBar.Error(clipErr, " or change 'clipboard' option")
}
screen.Events = make(chan tcell.Event)
// Here is the event loop which runs in a separate thread
go func() {
@@ -321,7 +363,7 @@ func main() {
e := screen.Screen.PollEvent()
screen.Unlock()
if e != nil {
events <- e
screen.Events <- e
}
}
}()
@@ -334,15 +376,12 @@ func main() {
// wait for initial resize event
select {
case event := <-events:
case event := <-screen.Events:
action.Tabs.HandleEvent(event)
case <-time.After(10 * time.Millisecond):
// time out after 10ms
}
// Since this loop is very slow (waits for user input every time) it's
// okay to be inefficient and run it via a function every time
// We do this so we can recover from panics without crashing the editor
for {
DoEvent()
}
@@ -352,16 +391,6 @@ func main() {
func DoEvent() {
var event tcell.Event
// recover from errors without crashing the editor
defer func() {
if err := recover(); err != nil {
if e, ok := err.(*lua.ApiError); ok {
screen.TermMessage("Lua API error:", e)
} else {
screen.TermMessage("Micro encountered an error:", errors.Wrap(err, 2).ErrorStack(), "\nIf you can reproduce this error, please report it at https://github.com/zyedidia/micro/issues")
}
}
}()
// Display everything
screen.Screen.Fill(' ', config.DefStyle)
screen.Screen.HideCursor()
@@ -377,22 +406,30 @@ func DoEvent() {
select {
case f := <-shell.Jobs:
// If a new job has finished while running in the background we should execute the callback
ulua.Lock.Lock()
f.Function(f.Output, f.Args)
ulua.Lock.Unlock()
case <-config.Autosave:
ulua.Lock.Lock()
for _, b := range buffer.OpenBuffers {
b.Save()
}
ulua.Lock.Unlock()
case <-shell.CloseTerms:
case event = <-events:
case event = <-screen.Events:
case <-screen.DrawChan():
for len(screen.DrawChan()) > 0 {
<-screen.DrawChan()
}
}
ulua.Lock.Lock()
// if event != nil {
if action.InfoBar.HasPrompt {
action.InfoBar.HandleEvent(event)
} else {
action.Tabs.HandleEvent(event)
}
// }
ulua.Lock.Unlock()
}

View File

@@ -13,14 +13,14 @@ import (
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
var tempDir string
var sim tcell.SimulationScreen
func init() {
events = make(chan tcell.Event, 8)
screen.Events = make(chan tcell.Event, 8)
}
func startup(args []string) (tcell.SimulationScreen, error) {
@@ -106,7 +106,7 @@ func handleEvent() {
e := screen.Screen.PollEvent()
screen.Unlock()
if e != nil {
events <- e
screen.Events <- e
}
DoEvent()
}

4
go.mod
View File

@@ -12,12 +12,12 @@ require (
github.com/sergi/go-diff v1.1.0
github.com/stretchr/testify v1.4.0
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb
github.com/zyedidia/clipboard v0.0.0-20200421031010-7c45b8673834
github.com/zyedidia/clipboard v1.0.3
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3
github.com/zyedidia/highlight v0.0.0-20170330143449-201131ce5cf5
github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d
github.com/zyedidia/pty v2.0.0+incompatible // indirect
github.com/zyedidia/tcell v1.4.8
github.com/zyedidia/tcell/v2 v2.0.2
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415
golang.org/x/text v0.3.2
gopkg.in/sourcemap.v1 v1.0.5 // indirect

10
go.sum
View File

@@ -35,11 +35,13 @@ github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/xo/terminfo v0.0.0-20200218205459-454e5b68f9e8 h1:woqigIZtZUZxws1zZA99nAvuz2mQrxtWsuZSR9c8I/A=
github.com/xo/terminfo v0.0.0-20200218205459-454e5b68f9e8/go.mod h1:6Yhx5ZJl5942QrNRWLwITArVT9okUXc5c3brgWJMoDc=
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/zyedidia/clipboard v0.0.0-20200421031010-7c45b8673834 h1:0nOfq3JwYRiY3+nwfWVQYEaXDmGCQgj3RKoqTifLzP4=
github.com/zyedidia/clipboard v0.0.0-20200421031010-7c45b8673834/go.mod h1:zykFnZUXX0ErxqvYLUFEq7QDJKId8rmh2FgD0/Y8cjA=
github.com/zyedidia/clipboard v1.0.3 h1:F/nCDVYMdbDWTmY8s8cJl0tnwX32q96IF09JHM14bUI=
github.com/zyedidia/clipboard v1.0.3/go.mod h1:zykFnZUXX0ErxqvYLUFEq7QDJKId8rmh2FgD0/Y8cjA=
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 h1:oMHjjTLfGXVuyOQBYj5/td9WC0mw4g1xDBPovIqmHew=
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3/go.mod h1:YKbIYP//Eln8eDgAJGI3IDvR3s4Tv9Z9TGIOumiyQ5c=
github.com/zyedidia/go-shellquote v0.0.0-20200613203517-eccd813c0655 h1:Z3RhH6hvcSx7eX6Q/pP6YVsgea/1eMDG99vtWwi3nK4=
@@ -52,8 +54,8 @@ github.com/zyedidia/poller v1.0.1 h1:Tt9S3AxAjXwWGNiC2TUdRJkQDZSzCBNVQ4xXiQ7440s
github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE=
github.com/zyedidia/pty v2.0.0+incompatible h1:Ou5vXL6tvjst+RV8sUFISbuKDnUJPhnpygApMFGweqw=
github.com/zyedidia/pty v2.0.0+incompatible/go.mod h1:4y9l9yJZNxRa7GB/fB+mmDmGkG3CqmzLf4vUxGGotEA=
github.com/zyedidia/tcell v1.4.8 h1:s4zYGOyCNDK4cdrgNVME0SxGizuT/oKY3OyB4Ls2Qpg=
github.com/zyedidia/tcell v1.4.8/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA=
github.com/zyedidia/tcell/v2 v2.0.2 h1:Zuf6vic3Yj2tFMXHCGOGL4WjqR3BwG30mOdwEz+ScxM=
github.com/zyedidia/tcell/v2 v2.0.2/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws=
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415 h1:752dTQ5OatJ9M5ULK2+9lor+nzyZz+LYDo3WGngg3Rc=
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415/go.mod h1:8leT8G0Cm8NoJHdrrKHyR9MirWoF4YW7pZh06B6H+1E=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@@ -7,13 +7,13 @@ import (
"time"
shellquote "github.com/kballard/go-shellquote"
"github.com/zyedidia/clipboard"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/clipboard"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
// ScrollUp is not an action
@@ -59,7 +59,7 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
h.doubleClick = false
h.Cursor.SelectLine()
h.Cursor.CopySelection("primary")
h.Cursor.CopySelection(clipboard.PrimaryReg)
} else {
// Double click
h.lastClickTime = time.Now()
@@ -68,7 +68,7 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
h.tripleClick = false
h.Cursor.SelectWord()
h.Cursor.CopySelection("primary")
h.Cursor.CopySelection(clipboard.PrimaryReg)
}
} else {
h.doubleClick = false
@@ -92,6 +92,7 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
h.Cursor.StoreVisualX()
h.lastLoc = mouseLoc
h.Relocate()
return true
}
@@ -961,13 +962,9 @@ func (h *BufPane) Redo() bool {
// Copy the selection to the system clipboard
func (h *BufPane) Copy() bool {
if h.Cursor.HasSelection() {
h.Cursor.CopySelection("clipboard")
h.Cursor.CopySelection(clipboard.ClipboardReg)
h.freshClip = true
if clipboard.Unsupported {
InfoBar.Message("Copied selection (install xclip for external clipboard)")
} else {
InfoBar.Message("Copied selection")
}
InfoBar.Message("Copied selection")
}
h.Relocate()
return true
@@ -979,13 +976,9 @@ func (h *BufPane) CopyLine() bool {
return false
} else {
h.Cursor.SelectLine()
h.Cursor.CopySelection("clipboard")
h.Cursor.CopySelection(clipboard.ClipboardReg)
h.freshClip = true
if clipboard.Unsupported {
InfoBar.Message("Copied line (install xclip for external clipboard)")
} else {
InfoBar.Message("Copied line")
}
InfoBar.Message("Copied line")
}
h.Cursor.Deselect(true)
h.Relocate()
@@ -1000,10 +993,10 @@ func (h *BufPane) CutLine() bool {
}
if h.freshClip == true {
if h.Cursor.HasSelection() {
if clip, err := clipboard.ReadAll("clipboard"); err != nil {
// messenger.Error(err)
if clip, err := clipboard.Read(clipboard.ClipboardReg); err != nil {
InfoBar.Error(err)
} else {
clipboard.WriteAll(clip+string(h.Cursor.GetSelection()), "clipboard")
clipboard.WriteMulti(clip+string(h.Cursor.GetSelection()), clipboard.ClipboardReg, h.Cursor.Num, h.Buf.NumCursors())
}
}
} else if time.Since(h.lastCutTime)/time.Second > 10*time.Second || h.freshClip == false {
@@ -1021,7 +1014,7 @@ func (h *BufPane) CutLine() bool {
// Cut the selection to the system clipboard
func (h *BufPane) Cut() bool {
if h.Cursor.HasSelection() {
h.Cursor.CopySelection("clipboard")
h.Cursor.CopySelection(clipboard.ClipboardReg)
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
h.freshClip = true
@@ -1147,16 +1140,24 @@ func (h *BufPane) MoveLinesDown() bool {
// Paste whatever is in the system clipboard into the buffer
// Delete and paste if the user has a selection
func (h *BufPane) Paste() bool {
clip, _ := clipboard.ReadAll("clipboard")
h.paste(clip)
clip, err := clipboard.ReadMulti(clipboard.ClipboardReg, h.Cursor.Num, h.Buf.NumCursors())
if err != nil {
InfoBar.Error(err)
} else {
h.paste(clip)
}
h.Relocate()
return true
}
// PastePrimary pastes from the primary clipboard (only use on linux)
func (h *BufPane) PastePrimary() bool {
clip, _ := clipboard.ReadAll("primary")
h.paste(clip)
clip, err := clipboard.ReadMulti(clipboard.PrimaryReg, h.Cursor.Num, h.Buf.NumCursors())
if err != nil {
InfoBar.Error(err)
} else {
h.paste(clip)
}
h.Relocate()
return true
}
@@ -1177,11 +1178,7 @@ func (h *BufPane) paste(clip string) {
h.Buf.Insert(h.Cursor.Loc, clip)
// h.Cursor.Loc = h.Cursor.Loc.Move(Count(clip), h.Buf)
h.freshClip = false
if clipboard.Unsupported {
InfoBar.Message("Pasted clipboard (install xclip for external clipboard)")
} else {
InfoBar.Message("Pasted clipboard")
}
InfoBar.Message("Pasted clipboard")
}
// JumpToMatchingBrace moves the cursor to the matching brace if it is
@@ -1443,6 +1440,18 @@ func (h *BufPane) Escape() bool {
return true
}
// Deselect deselects on the current cursor
func (h *BufPane) Deselect() bool {
h.Cursor.Deselect(true)
return true
}
// ClearInfo clears the infobar
func (h *BufPane) ClearInfo() bool {
InfoBar.Message("")
return true
}
// Quit this will close the current tab or view that is open
func (h *BufPane) Quit() bool {
quit := func() {

View File

@@ -6,15 +6,22 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"unicode"
"github.com/zyedidia/json5"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
var Binder = map[string]func(e Event, action string){
"command": InfoMapEvent,
"buffer": BufMapEvent,
"terminal": TermMapEvent,
}
func createBindingsIfNotExist(fname string) {
if _, e := os.Stat(fname); os.IsNotExist(e) {
ioutil.WriteFile(fname, []byte("{}"), 0644)
@@ -23,10 +30,7 @@ func createBindingsIfNotExist(fname string) {
// InitBindings intializes the bindings map by reading from bindings.json
func InitBindings() {
config.Bindings = DefaultBindings()
var parsed map[string]string
defaults := DefaultBindings()
var parsed map[string]interface{}
filename := filepath.Join(config.ConfigDir, "bindings.json")
createBindingsIfNotExist(filename)
@@ -44,34 +48,87 @@ func InitBindings() {
}
}
for k, v := range defaults {
BindKey(k, v)
for p, bind := range Binder {
defaults := DefaultBindings(p)
for k, v := range defaults {
BindKey(k, v, bind)
}
}
for k, v := range parsed {
BindKey(k, v)
switch val := v.(type) {
case string:
BindKey(k, val, Binder["buffer"])
case map[string]interface{}:
bind := Binder[k]
for e, a := range val {
s, ok := a.(string)
if !ok {
screen.TermMessage("Error reading bindings.json: non-string and non-map entry", k)
} else {
BindKey(e, s, bind)
}
}
default:
screen.TermMessage("Error reading bindings.json: non-string and non-map entry", k)
}
}
}
func BindKey(k, v string) {
event, ok := findEvent(k)
if !ok {
screen.TermMessage(k, "is not a bindable event")
func BindKey(k, v string, bind func(e Event, a string)) {
event, err := findEvent(k)
if err != nil {
screen.TermMessage(err)
return
}
switch e := event.(type) {
case KeyEvent:
BufMapKey(e, v)
case MouseEvent:
BufMapMouse(e, v)
case RawEvent:
BufMapKey(e, v)
}
config.Bindings[event.Name()] = v
config.Bindings[k] = v
bind(event, v)
// switch e := event.(type) {
// case KeyEvent:
// InfoMapKey(e, v)
// case KeySequenceEvent:
// InfoMapKey(e, v)
// case MouseEvent:
// InfoMapMouse(e, v)
// case RawEvent:
// InfoMapKey(e, v)
// }
}
// findEvent will find binding Key 'b' using string 'k'
func findEvent(k string) (b Event, ok bool) {
var r = regexp.MustCompile("<(.+?)>")
func findEvents(k string) (b KeySequenceEvent, ok bool, err error) {
var events []Event = nil
for len(k) > 0 {
groups := r.FindStringSubmatchIndex(k)
if len(groups) > 3 {
if events == nil {
events = make([]Event, 0, 3)
}
e, ok := findSingleEvent(k[groups[2]:groups[3]])
if !ok {
return KeySequenceEvent{}, false, errors.New("Invalid event " + k[groups[2]:groups[3]])
}
events = append(events, e)
k = k[groups[3]+1:]
} else {
return KeySequenceEvent{}, false, nil
}
}
return KeySequenceEvent{events}, true, nil
}
// findSingleEvent will find binding Key 'b' using string 'k'
func findSingleEvent(k string) (b Event, ok bool) {
modifiers := tcell.ModNone
// First, we'll strip off all the modifiers in the name and add them to the
@@ -162,6 +219,23 @@ modSearch:
return KeyEvent{}, false
}
func findEvent(k string) (Event, error) {
var event Event
event, ok, err := findEvents(k)
if err != nil {
return nil, err
}
if !ok {
event, ok = findSingleEvent(k)
if !ok {
return nil, errors.New(k + " is not a bindable event")
}
}
return event, nil
}
// TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
// Returns true if the keybinding already existed and a possible error
func TryBindKey(k, v string, overwrite bool) (bool, error) {
@@ -181,14 +255,14 @@ func TryBindKey(k, v string, overwrite bool) (bool, error) {
return false, errors.New("Error reading bindings.json: " + err.Error())
}
key, ok := findEvent(k)
if !ok {
return false, errors.New("Invalid event " + k)
key, err := findEvent(k)
if err != nil {
return false, err
}
found := false
for ev := range parsed {
if e, ok := findEvent(ev); ok {
if e, err := findEvent(ev); err == nil {
if e == key {
if overwrite {
parsed[ev] = v
@@ -205,7 +279,7 @@ func TryBindKey(k, v string, overwrite bool) (bool, error) {
parsed[k] = v
}
BindKey(k, v)
BindKey(k, v, Binder["buffer"])
txt, _ := json.MarshalIndent(parsed, "", " ")
return true, ioutil.WriteFile(filename, append(txt, '\n'), 0644)
@@ -231,13 +305,13 @@ func UnbindKey(k string) error {
return errors.New("Error reading bindings.json: " + err.Error())
}
key, ok := findEvent(k)
if !ok {
return errors.New("Invalid event " + k)
key, err := findEvent(k)
if err != nil {
return err
}
for ev := range parsed {
if e, ok := findEvent(ev); ok {
if e, err := findEvent(ev); err == nil {
if e == key {
delete(parsed, ev)
break
@@ -245,9 +319,9 @@ func UnbindKey(k string) error {
}
}
defaults := DefaultBindings()
defaults := DefaultBindings("buffer")
if a, ok := defaults[k]; ok {
BindKey(k, a)
BindKey(k, a, Binder["buffer"])
} else if _, ok := config.Bindings[k]; ok {
BufUnmap(key)
delete(config.Bindings, k)
@@ -260,9 +334,9 @@ func UnbindKey(k string) error {
}
var mouseEvents = map[string]tcell.ButtonMask{
"MouseLeft": tcell.Button1,
"MouseMiddle": tcell.Button2,
"MouseRight": tcell.Button3,
"MouseLeft": tcell.ButtonPrimary,
"MouseMiddle": tcell.ButtonMiddle,
"MouseRight": tcell.ButtonSecondary,
"MouseWheelUp": tcell.WheelUp,
"MouseWheelDown": tcell.WheelDown,
"MouseWheelLeft": tcell.WheelLeft,

View File

@@ -8,24 +8,33 @@ import (
lua "github.com/yuin/gopher-lua"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/clipboard"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/display"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
type BufKeyAction func(*BufPane) bool
type BufMouseAction func(*BufPane, *tcell.EventMouse) bool
var BufKeyBindings map[Event]BufKeyAction
var BufKeyStrings map[Event]string
var BufMouseBindings map[MouseEvent]BufMouseAction
var BufBindings *KeyTree
func BufKeyActionGeneral(a BufKeyAction) PaneKeyAction {
return func(p Pane) bool {
return a(p.(*BufPane))
}
}
func BufMouseActionGeneral(a BufMouseAction) PaneMouseAction {
return func(p Pane, me *tcell.EventMouse) bool {
return a(p.(*BufPane), me)
}
}
func init() {
BufKeyBindings = make(map[Event]BufKeyAction)
BufKeyStrings = make(map[Event]string)
BufMouseBindings = make(map[MouseEvent]BufMouseAction)
BufBindings = NewKeyTree()
}
func LuaAction(fn string) func(*BufPane) bool {
@@ -51,9 +60,17 @@ func LuaAction(fn string) func(*BufPane) bool {
}
}
// BufMapKey maps a key event to an action
func BufMapKey(k Event, action string) {
BufKeyStrings[k] = action
// BufMapKey maps an event to an action
func BufMapEvent(k Event, action string) {
switch e := k.(type) {
case KeyEvent, KeySequenceEvent, RawEvent:
bufMapKey(e, action)
case MouseEvent:
bufMapMouse(e, action)
}
}
func bufMapKey(k Event, action string) {
var actionfns []func(*BufPane) bool
var names []string
var types []byte
@@ -108,10 +125,11 @@ func BufMapKey(k Event, action string) {
}
actionfns = append(actionfns, afn)
}
BufKeyBindings[k] = func(h *BufPane) bool {
bufAction := func(h *BufPane) bool {
cursors := h.Buf.GetCursors()
success := true
for i, a := range actionfns {
innerSuccess := true
for j, c := range cursors {
if c == nil {
continue
@@ -119,37 +137,41 @@ func BufMapKey(k Event, action string) {
h.Buf.SetCurCursor(c.Num)
h.Cursor = c
if i == 0 || (success && types[i-1] == '&') || (!success && types[i-1] == '|') || (types[i-1] == ',') {
success = h.execAction(a, names[i], j)
innerSuccess = innerSuccess && h.execAction(a, names[i], j)
} else {
break
}
}
// if the action changed the current pane, update the reference
h = MainTab().CurPane()
success = innerSuccess
}
return true
}
BufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(bufAction))
}
// BufMapMouse maps a mouse event to an action
func BufMapMouse(k MouseEvent, action string) {
func bufMapMouse(k MouseEvent, action string) {
if f, ok := BufMouseActions[action]; ok {
BufMouseBindings[k] = f
BufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f))
} else {
delete(BufMouseBindings, k)
BufMapKey(k, action)
// TODO
// delete(BufMouseBindings, k)
bufMapKey(k, action)
}
}
// BufUnmap unmaps a key or mouse event from any action
func BufUnmap(k Event) {
delete(BufKeyBindings, k)
delete(BufKeyStrings, k)
switch e := k.(type) {
case MouseEvent:
delete(BufMouseBindings, e)
}
// TODO
// delete(BufKeyBindings, k)
//
// switch e := k.(type) {
// case MouseEvent:
// delete(BufMouseBindings, e)
// }
}
// The BufPane connects the buffer and the window
@@ -160,9 +182,13 @@ func BufUnmap(k Event) {
type BufPane struct {
display.BWindow
// Buf is the buffer this BufPane views
Buf *buffer.Buffer
// Bindings stores the association of key events and actions
bindings *KeyTree
Cursor *buffer.Cursor // the active cursor
// Cursor is the currently active buffer cursor
Cursor *buffer.Cursor
// Since tcell doesn't differentiate between a mouse release event
// and a mouse move event with no keys pressed, we need to keep
@@ -319,7 +345,7 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
case *tcell.EventKey:
ke := KeyEvent{
code: e.Key(),
mod: e.Modifiers(),
mod: metaToAlt(e.Modifiers()),
r: e.Rune(),
}
@@ -360,7 +386,7 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
// h.Cursor.SetSelectionEnd(h.Cursor.Loc)
// }
if h.Cursor.HasSelection() {
h.Cursor.CopySelection("primary")
h.Cursor.CopySelection(clipboard.PrimaryReg)
}
h.mouseReleased = true
}
@@ -369,7 +395,7 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
if !cancel {
me := MouseEvent{
btn: e.Buttons(),
mod: e.Modifiers(),
mod: metaToAlt(e.Modifiers()),
}
h.DoMouseEvent(me, e)
}
@@ -393,13 +419,26 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
}
}
func (h *BufPane) Bindings() *KeyTree {
if h.bindings != nil {
return h.bindings
}
return BufBindings
}
// DoKeyEvent executes a key event by finding the action it is bound
// to and executing it (possibly multiple times for multiple cursors)
func (h *BufPane) DoKeyEvent(e Event) bool {
if action, ok := BufKeyBindings[e]; ok {
return action(h)
binds := h.Bindings()
action, more := binds.NextEvent(e, nil)
if action != nil && !more {
action(h)
binds.ResetEvents()
return true
} else if action == nil && !more {
binds.ResetEvents()
}
return false
return more
}
func (h *BufPane) execAction(action func(*BufPane) bool, name string, cursor int) bool {
@@ -433,22 +472,34 @@ func (h *BufPane) completeAction(action string) {
}
func (h *BufPane) HasKeyEvent(e Event) bool {
_, ok := BufKeyBindings[e]
return ok
// TODO
return true
// _, ok := BufKeyBindings[e]
// return ok
}
// DoMouseEvent executes a mouse event by finding the action it is bound
// to and executing it
func (h *BufPane) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
if action, ok := BufMouseBindings[e]; ok {
if action(h, te) {
h.Relocate()
}
binds := h.Bindings()
action, _ := binds.NextEvent(e, te)
if action != nil {
action(h)
binds.ResetEvents()
return true
} else if h.HasKeyEvent(e) {
return h.DoKeyEvent(e)
}
// TODO
return false
// if action, ok := BufMouseBindings[e]; ok {
// if action(h, te) {
// h.Relocate()
// }
// return true
// } else if h.HasKeyEvent(e) {
// return h.DoKeyEvent(e)
// }
// return false
}
// DoRuneInsert inserts a given rune into the current buffer
@@ -635,6 +686,8 @@ var BufKeyActions = map[string]BufKeyAction{
"SkipMultiCursor": (*BufPane).SkipMultiCursor,
"JumpToMatchingBrace": (*BufPane).JumpToMatchingBrace,
"JumpLine": (*BufPane).JumpLine,
"Deselect": (*BufPane).Deselect,
"ClearInfo": (*BufPane).ClearInfo,
"None": (*BufPane).None,
// This was changed to InsertNewline but I don't want to break backwards compatibility
@@ -686,6 +739,7 @@ var MultiActions = map[string]bool{
"FindNext": true,
"FindPrevious": true,
"CopyLine": true,
"Copy": true,
"Cut": true,
"CutLine": true,
"DuplicateLine": true,

View File

@@ -13,6 +13,7 @@ import (
shellquote "github.com/kballard/go-shellquote"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/clipboard"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
@@ -505,6 +506,12 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error {
}
} else if option == "paste" {
screen.Screen.SetPaste(nativeValue.(bool))
} else if option == "clipboard" {
m := clipboard.SetMethod(nativeValue.(string))
err := clipboard.Initialize(m)
if err != nil {
return err
}
} else {
for _, pl := range config.Plugins {
if option == pl.Name {
@@ -634,7 +641,12 @@ func (h *BufPane) ShowKeyCmd(args []string) {
return
}
if action, ok := config.Bindings[args[0]]; ok {
event, err := findEvent(args[0])
if err != nil {
InfoBar.Error(err)
return
}
if action, ok := config.Bindings[event.Name()]; ok {
InfoBar.Message(action)
} else {
InfoBar.Message(args[0], " has no binding")

View File

@@ -0,0 +1,21 @@
package action
var termdefaults = map[string]string{
"<Ctrl-q><Ctrl-q>": "Exit",
"<Ctrl-e><Ctrl-e>": "CommandMode",
"<Ctrl-w><Ctrl-w>": "NextSplit",
}
// DefaultBindings returns a map containing micro's default keybindings
func DefaultBindings(pane string) map[string]string {
switch pane {
case "command":
return infodefaults
case "buffer":
return bufdefaults
case "terminal":
return termdefaults
default:
return map[string]string{}
}
}

View File

@@ -1,108 +1,179 @@
package action
// DefaultBindings returns a map containing micro's default keybindings
func DefaultBindings() map[string]string {
return map[string]string{
"Up": "CursorUp",
"Down": "CursorDown",
"Right": "CursorRight",
"Left": "CursorLeft",
"ShiftUp": "SelectUp",
"ShiftDown": "SelectDown",
"ShiftLeft": "SelectLeft",
"ShiftRight": "SelectRight",
"AltLeft": "WordLeft",
"AltRight": "WordRight",
"AltUp": "MoveLinesUp",
"AltDown": "MoveLinesDown",
"AltShiftRight": "SelectWordRight",
"AltShiftLeft": "SelectWordLeft",
"CtrlLeft": "StartOfTextToggle",
"CtrlRight": "EndOfLine",
"CtrlShiftLeft": "SelectToStartOfTextToggle",
"ShiftHome": "SelectToStartOfTextToggle",
"CtrlShiftRight": "SelectToEndOfLine",
"ShiftEnd": "SelectToEndOfLine",
"CtrlUp": "CursorStart",
"CtrlDown": "CursorEnd",
"CtrlShiftUp": "SelectToStart",
"CtrlShiftDown": "SelectToEnd",
"Alt-{": "ParagraphPrevious",
"Alt-}": "ParagraphNext",
"Enter": "InsertNewline",
"CtrlH": "Backspace",
"Backspace": "Backspace",
"OldBackspace": "Backspace",
"Alt-CtrlH": "DeleteWordLeft",
"Alt-Backspace": "DeleteWordLeft",
"Tab": "Autocomplete|IndentSelection|InsertTab",
"Backtab": "CycleAutocompleteBack|OutdentSelection|OutdentLine",
"Ctrl-o": "OpenFile",
"Ctrl-s": "Save",
"Ctrl-f": "Find",
"Ctrl-n": "FindNext",
"Ctrl-p": "FindPrevious",
"Ctrl-z": "Undo",
"Ctrl-y": "Redo",
"Ctrl-c": "CopyLine|Copy",
"Ctrl-x": "Cut",
"Ctrl-k": "CutLine",
"Ctrl-d": "DuplicateLine",
"Ctrl-v": "Paste",
"Ctrl-a": "SelectAll",
"Ctrl-t": "AddTab",
"Alt-,": "PreviousTab",
"Alt-.": "NextTab",
"Home": "StartOfTextToggle",
"End": "EndOfLine",
"CtrlHome": "CursorStart",
"CtrlEnd": "CursorEnd",
"PageUp": "CursorPageUp",
"PageDown": "CursorPageDown",
"CtrlPageUp": "PreviousTab",
"CtrlPageDown": "NextTab",
"Ctrl-g": "ToggleHelp",
"Alt-g": "ToggleKeyMenu",
"Ctrl-r": "ToggleRuler",
"Ctrl-l": "command-edit:goto ",
"Delete": "Delete",
"Ctrl-b": "ShellMode",
"Ctrl-q": "Quit",
"Ctrl-e": "CommandMode",
"Ctrl-w": "NextSplit",
"Ctrl-u": "ToggleMacro",
"Ctrl-j": "PlayMacro",
"Insert": "ToggleOverwriteMode",
var bufdefaults = map[string]string{
"Up": "CursorUp",
"Down": "CursorDown",
"Right": "CursorRight",
"Left": "CursorLeft",
"ShiftUp": "SelectUp",
"ShiftDown": "SelectDown",
"ShiftLeft": "SelectLeft",
"ShiftRight": "SelectRight",
"AltLeft": "WordLeft",
"AltRight": "WordRight",
"AltUp": "MoveLinesUp",
"AltDown": "MoveLinesDown",
"AltShiftRight": "SelectWordRight",
"AltShiftLeft": "SelectWordLeft",
"CtrlLeft": "StartOfTextToggle",
"CtrlRight": "EndOfLine",
"CtrlShiftLeft": "SelectToStartOfTextToggle",
"ShiftHome": "SelectToStartOfTextToggle",
"CtrlShiftRight": "SelectToEndOfLine",
"ShiftEnd": "SelectToEndOfLine",
"CtrlUp": "CursorStart",
"CtrlDown": "CursorEnd",
"CtrlShiftUp": "SelectToStart",
"CtrlShiftDown": "SelectToEnd",
"Alt-{": "ParagraphPrevious",
"Alt-}": "ParagraphNext",
"Enter": "InsertNewline",
"CtrlH": "Backspace",
"Backspace": "Backspace",
"OldBackspace": "Backspace",
"Alt-CtrlH": "DeleteWordLeft",
"Alt-Backspace": "DeleteWordLeft",
"Tab": "Autocomplete|IndentSelection|InsertTab",
"Backtab": "CycleAutocompleteBack|OutdentSelection|OutdentLine",
"Ctrl-o": "OpenFile",
"Ctrl-s": "Save",
"Ctrl-f": "Find",
"Ctrl-n": "FindNext",
"Ctrl-p": "FindPrevious",
"Ctrl-z": "Undo",
"Ctrl-y": "Redo",
"Ctrl-c": "CopyLine|Copy",
"Ctrl-x": "Cut",
"Ctrl-k": "CutLine",
"Ctrl-d": "DuplicateLine",
"Ctrl-v": "Paste",
"Ctrl-a": "SelectAll",
"Ctrl-t": "AddTab",
"Alt-,": "PreviousTab",
"Alt-.": "NextTab",
"Home": "StartOfTextToggle",
"End": "EndOfLine",
"CtrlHome": "CursorStart",
"CtrlEnd": "CursorEnd",
"PageUp": "CursorPageUp",
"PageDown": "CursorPageDown",
"CtrlPageUp": "PreviousTab",
"CtrlPageDown": "NextTab",
"Ctrl-g": "ToggleHelp",
"Alt-g": "ToggleKeyMenu",
"Ctrl-r": "ToggleRuler",
"Ctrl-l": "command-edit:goto ",
"Delete": "Delete",
"Ctrl-b": "ShellMode",
"Ctrl-q": "Quit",
"Ctrl-e": "CommandMode",
"Ctrl-w": "NextSplit",
"Ctrl-u": "ToggleMacro",
"Ctrl-j": "PlayMacro",
"Insert": "ToggleOverwriteMode",
// Emacs-style keybindings
"Alt-f": "WordRight",
"Alt-b": "WordLeft",
"Alt-a": "StartOfText",
"Alt-e": "EndOfLine",
// "Alt-p": "CursorUp",
// "Alt-n": "CursorDown",
// Emacs-style keybindings
"Alt-f": "WordRight",
"Alt-b": "WordLeft",
"Alt-a": "StartOfText",
"Alt-e": "EndOfLine",
// "Alt-p": "CursorUp",
// "Alt-n": "CursorDown",
// Integration with file managers
"F2": "Save",
"F3": "Find",
"F4": "Quit",
"F7": "Find",
"F10": "Quit",
"Esc": "Escape",
// Integration with file managers
"F2": "Save",
"F3": "Find",
"F4": "Quit",
"F7": "Find",
"F10": "Quit",
"Esc": "Escape,Deselect,ClearInfo,RemoveAllMultiCursors",
// Mouse bindings
"MouseWheelUp": "ScrollUp",
"MouseWheelDown": "ScrollDown",
"MouseLeft": "MousePress",
"MouseMiddle": "PastePrimary",
"Ctrl-MouseLeft": "MouseMultiCursor",
// Mouse bindings
"MouseWheelUp": "ScrollUp",
"MouseWheelDown": "ScrollDown",
"MouseLeft": "MousePress",
"MouseMiddle": "PastePrimary",
"Ctrl-MouseLeft": "MouseMultiCursor",
"Alt-n": "SpawnMultiCursor",
"AltShiftUp": "SpawnMultiCursorUp",
"AltShiftDown": "SpawnMultiCursorDown",
"Alt-m": "SpawnMultiCursorSelect",
"Alt-p": "RemoveMultiCursor",
"Alt-c": "RemoveAllMultiCursors",
"Alt-x": "SkipMultiCursor",
}
"Alt-n": "SpawnMultiCursor",
"AltShiftUp": "SpawnMultiCursorUp",
"AltShiftDown": "SpawnMultiCursorDown",
"Alt-m": "SpawnMultiCursorSelect",
"Alt-p": "RemoveMultiCursor",
"Alt-c": "RemoveAllMultiCursors",
"Alt-x": "SkipMultiCursor",
}
var infodefaults = map[string]string{
"Up": "HistoryUp",
"Down": "HistoryDown",
"Right": "CursorRight",
"Left": "CursorLeft",
"ShiftUp": "SelectUp",
"ShiftDown": "SelectDown",
"ShiftLeft": "SelectLeft",
"ShiftRight": "SelectRight",
"AltLeft": "WordLeft",
"AltRight": "WordRight",
"AltUp": "CursorStart",
"AltDown": "CursorEnd",
"AltShiftRight": "SelectWordRight",
"AltShiftLeft": "SelectWordLeft",
"CtrlLeft": "StartOfTextToggle",
"CtrlRight": "EndOfLine",
"CtrlShiftLeft": "SelectToStartOfTextToggle",
"ShiftHome": "SelectToStartOfTextToggle",
"CtrlShiftRight": "SelectToEndOfLine",
"ShiftEnd": "SelectToEndOfLine",
"CtrlUp": "CursorStart",
"CtrlDown": "CursorEnd",
"CtrlShiftUp": "SelectToStart",
"CtrlShiftDown": "SelectToEnd",
"Enter": "ExecuteCommand",
"CtrlH": "Backspace",
"Backspace": "Backspace",
"OldBackspace": "Backspace",
"Alt-CtrlH": "DeleteWordLeft",
"Alt-Backspace": "DeleteWordLeft",
"Tab": "CommandComplete",
"Backtab": "CycleAutocompleteBack",
"Ctrl-z": "Undo",
"Ctrl-y": "Redo",
"Ctrl-c": "CopyLine|Copy",
"Ctrl-x": "Cut",
"Ctrl-k": "CutLine",
"Ctrl-v": "Paste",
"Home": "StartOfTextToggle",
"End": "EndOfLine",
"CtrlHome": "CursorStart",
"CtrlEnd": "CursorEnd",
"Delete": "Delete",
"Ctrl-q": "AbortCommand",
"Ctrl-e": "EndOfLine",
"Ctrl-a": "StartOfLine",
"Ctrl-w": "DeleteWordLeft",
"Insert": "ToggleOverwriteMode",
"Ctrl-b": "WordLeft",
"Ctrl-f": "WordRight",
"Ctrl-d": "DeleteWordLeft",
"Ctrl-m": "ExecuteCommand",
"Ctrl-n": "HistoryDown",
"Ctrl-p": "HistoryUp",
"Ctrl-u": "SelectToStart",
// Emacs-style keybindings
"Alt-f": "WordRight",
"Alt-b": "WordLeft",
"Alt-a": "StartOfText",
"Alt-e": "EndOfLine",
// Integration with file managers
"F10": "AbortCommand",
"Esc": "AbortCommand",
// Mouse bindings
"MouseWheelUp": "HistoryUp",
"MouseWheelDown": "HistoryDown",
"MouseLeft": "MousePress",
"MouseMiddle": "PastePrimary",
}

View File

@@ -2,109 +2,180 @@
package action
// DefaultBindings returns a map containing micro's default keybindings
func DefaultBindings() map[string]string {
return map[string]string{
"Up": "CursorUp",
"Down": "CursorDown",
"Right": "CursorRight",
"Left": "CursorLeft",
"ShiftUp": "SelectUp",
"ShiftDown": "SelectDown",
"ShiftLeft": "SelectLeft",
"ShiftRight": "SelectRight",
"CtrlLeft": "WordLeft",
"CtrlRight": "WordRight",
"AltUp": "MoveLinesUp",
"AltDown": "MoveLinesDown",
"CtrlShiftRight": "SelectWordRight",
"CtrlShiftLeft": "SelectWordLeft",
"AltLeft": "StartOfTextToggle",
"AltRight": "EndOfLine",
"AltShiftLeft": "SelectToStartOfTextToggle",
"ShiftHome": "SelectToStartOfTextToggle",
"AltShiftRight": "SelectToEndOfLine",
"ShiftEnd": "SelectToEndOfLine",
"CtrlUp": "CursorStart",
"CtrlDown": "CursorEnd",
"CtrlShiftUp": "SelectToStart",
"CtrlShiftDown": "SelectToEnd",
"Alt-{": "ParagraphPrevious",
"Alt-}": "ParagraphNext",
"Enter": "InsertNewline",
"CtrlH": "Backspace",
"Backspace": "Backspace",
"OldBackspace": "Backspace",
"Alt-CtrlH": "DeleteWordLeft",
"Alt-Backspace": "DeleteWordLeft",
"Tab": "Autocomplete|IndentSelection|InsertTab",
"Backtab": "CycleAutocompleteBack|OutdentSelection|OutdentLine",
"Ctrl-o": "OpenFile",
"Ctrl-s": "Save",
"Ctrl-f": "Find",
"Ctrl-n": "FindNext",
"Ctrl-p": "FindPrevious",
"Ctrl-z": "Undo",
"Ctrl-y": "Redo",
"Ctrl-c": "CopyLine|Copy",
"Ctrl-x": "Cut",
"Ctrl-k": "CutLine",
"Ctrl-d": "DuplicateLine",
"Ctrl-v": "Paste",
"Ctrl-a": "SelectAll",
"Ctrl-t": "AddTab",
"Alt-,": "PreviousTab",
"Alt-.": "NextTab",
"Home": "StartOfTextToggle",
"End": "EndOfLine",
"CtrlHome": "CursorStart",
"CtrlEnd": "CursorEnd",
"PageUp": "CursorPageUp",
"PageDown": "CursorPageDown",
"CtrlPageUp": "PreviousTab",
"CtrlPageDown": "NextTab",
"Ctrl-g": "ToggleHelp",
"Alt-g": "ToggleKeyMenu",
"Ctrl-r": "ToggleRuler",
"Ctrl-l": "command-edit:goto ",
"Delete": "Delete",
"Ctrl-b": "ShellMode",
"Ctrl-q": "Quit",
"Ctrl-e": "CommandMode",
"Ctrl-w": "NextSplit",
"Ctrl-u": "ToggleMacro",
"Ctrl-j": "PlayMacro",
"Insert": "ToggleOverwriteMode",
var bufdefaults = map[string]string{
"Up": "CursorUp",
"Down": "CursorDown",
"Right": "CursorRight",
"Left": "CursorLeft",
"ShiftUp": "SelectUp",
"ShiftDown": "SelectDown",
"ShiftLeft": "SelectLeft",
"ShiftRight": "SelectRight",
"CtrlLeft": "WordLeft",
"CtrlRight": "WordRight",
"AltUp": "MoveLinesUp",
"AltDown": "MoveLinesDown",
"CtrlShiftRight": "SelectWordRight",
"CtrlShiftLeft": "SelectWordLeft",
"AltLeft": "StartOfTextToggle",
"AltRight": "EndOfLine",
"AltShiftLeft": "SelectToStartOfTextToggle",
"ShiftHome": "SelectToStartOfTextToggle",
"AltShiftRight": "SelectToEndOfLine",
"ShiftEnd": "SelectToEndOfLine",
"CtrlUp": "CursorStart",
"CtrlDown": "CursorEnd",
"CtrlShiftUp": "SelectToStart",
"CtrlShiftDown": "SelectToEnd",
"Alt-{": "ParagraphPrevious",
"Alt-}": "ParagraphNext",
"Enter": "InsertNewline",
"CtrlH": "Backspace",
"Backspace": "Backspace",
"OldBackspace": "Backspace",
"Alt-CtrlH": "DeleteWordLeft",
"Alt-Backspace": "DeleteWordLeft",
"Tab": "Autocomplete|IndentSelection|InsertTab",
"Backtab": "CycleAutocompleteBack|OutdentSelection|OutdentLine",
"Ctrl-o": "OpenFile",
"Ctrl-s": "Save",
"Ctrl-f": "Find",
"Ctrl-n": "FindNext",
"Ctrl-p": "FindPrevious",
"Ctrl-z": "Undo",
"Ctrl-y": "Redo",
"Ctrl-c": "CopyLine|Copy",
"Ctrl-x": "Cut",
"Ctrl-k": "CutLine",
"Ctrl-d": "DuplicateLine",
"Ctrl-v": "Paste",
"Ctrl-a": "SelectAll",
"Ctrl-t": "AddTab",
"Alt-,": "PreviousTab",
"Alt-.": "NextTab",
"Home": "StartOfTextToggle",
"End": "EndOfLine",
"CtrlHome": "CursorStart",
"CtrlEnd": "CursorEnd",
"PageUp": "CursorPageUp",
"PageDown": "CursorPageDown",
"CtrlPageUp": "PreviousTab",
"CtrlPageDown": "NextTab",
"Ctrl-g": "ToggleHelp",
"Alt-g": "ToggleKeyMenu",
"Ctrl-r": "ToggleRuler",
"Ctrl-l": "command-edit:goto ",
"Delete": "Delete",
"Ctrl-b": "ShellMode",
"Ctrl-q": "Quit",
"Ctrl-e": "CommandMode",
"Ctrl-w": "NextSplit",
"Ctrl-u": "ToggleMacro",
"Ctrl-j": "PlayMacro",
"Insert": "ToggleOverwriteMode",
// Emacs-style keybindings
"Alt-f": "WordRight",
"Alt-b": "WordLeft",
"Alt-a": "StartOfText",
"Alt-e": "EndOfLine",
// "Alt-p": "CursorUp",
// "Alt-n": "CursorDown",
// Emacs-style keybindings
"Alt-f": "WordRight",
"Alt-b": "WordLeft",
"Alt-a": "StartOfText",
"Alt-e": "EndOfLine",
// "Alt-p": "CursorUp",
// "Alt-n": "CursorDown",
// Integration with file managers
"F2": "Save",
"F3": "Find",
"F4": "Quit",
"F7": "Find",
"F10": "Quit",
"Esc": "Escape",
// Integration with file managers
"F2": "Save",
"F3": "Find",
"F4": "Quit",
"F7": "Find",
"F10": "Quit",
"Esc": "Escape,Deselect,ClearInfo,RemoveAllMultiCursors",
// Mouse bindings
"MouseWheelUp": "ScrollUp",
"MouseWheelDown": "ScrollDown",
"MouseLeft": "MousePress",
"MouseMiddle": "PastePrimary",
"Ctrl-MouseLeft": "MouseMultiCursor",
// Mouse bindings
"MouseWheelUp": "ScrollUp",
"MouseWheelDown": "ScrollDown",
"MouseLeft": "MousePress",
"MouseMiddle": "PastePrimary",
"Ctrl-MouseLeft": "MouseMultiCursor",
"Alt-n": "SpawnMultiCursor",
"Alt-m": "SpawnMultiCursorSelect",
"AltShiftUp": "SpawnMultiCursorUp",
"AltShiftDown": "SpawnMultiCursorDown",
"Alt-p": "RemoveMultiCursor",
"Alt-c": "RemoveAllMultiCursors",
"Alt-x": "SkipMultiCursor",
}
"Alt-n": "SpawnMultiCursor",
"Alt-m": "SpawnMultiCursorSelect",
"AltShiftUp": "SpawnMultiCursorUp",
"AltShiftDown": "SpawnMultiCursorDown",
"Alt-p": "RemoveMultiCursor",
"Alt-c": "RemoveAllMultiCursors",
"Alt-x": "SkipMultiCursor",
}
var infodefaults = map[string]string{
"Up": "HistoryUp",
"Down": "HistoryDown",
"Right": "CursorRight",
"Left": "CursorLeft",
"ShiftUp": "SelectUp",
"ShiftDown": "SelectDown",
"ShiftLeft": "SelectLeft",
"ShiftRight": "SelectRight",
"AltLeft": "StartOfTextToggle",
"AltRight": "EndOfLine",
"AltUp": "CursorStart",
"AltDown": "CursorEnd",
"AltShiftRight": "SelectWordRight",
"AltShiftLeft": "SelectWordLeft",
"CtrlLeft": "WordLeft",
"CtrlRight": "WordRight",
"CtrlShiftLeft": "SelectToStartOfTextToggle",
"ShiftHome": "SelectToStartOfTextToggle",
"CtrlShiftRight": "SelectToEndOfLine",
"ShiftEnd": "SelectToEndOfLine",
"CtrlUp": "CursorStart",
"CtrlDown": "CursorEnd",
"CtrlShiftUp": "SelectToStart",
"CtrlShiftDown": "SelectToEnd",
"Enter": "ExecuteCommand",
"CtrlH": "Backspace",
"Backspace": "Backspace",
"OldBackspace": "Backspace",
"Alt-CtrlH": "DeleteWordLeft",
"Alt-Backspace": "DeleteWordLeft",
"Tab": "CommandComplete",
"Backtab": "CycleAutocompleteBack",
"Ctrl-z": "Undo",
"Ctrl-y": "Redo",
"Ctrl-c": "CopyLine|Copy",
"Ctrl-x": "Cut",
"Ctrl-k": "CutLine",
"Ctrl-v": "Paste",
"Home": "StartOfTextToggle",
"End": "EndOfLine",
"CtrlHome": "CursorStart",
"CtrlEnd": "CursorEnd",
"Delete": "Delete",
"Ctrl-q": "AbortCommand",
"Ctrl-e": "EndOfLine",
"Ctrl-a": "StartOfLine",
"Ctrl-w": "DeleteWordLeft",
"Insert": "ToggleOverwriteMode",
"Ctrl-b": "WordLeft",
"Ctrl-f": "WordRight",
"Ctrl-d": "DeleteWordLeft",
"Ctrl-m": "ExecuteCommand",
"Ctrl-n": "HistoryDown",
"Ctrl-p": "HistoryUp",
"Ctrl-u": "SelectToStart",
// Emacs-style keybindings
"Alt-f": "WordRight",
"Alt-b": "WordLeft",
"Alt-a": "StartOfText",
"Alt-e": "EndOfLine",
// Integration with file managers
"F10": "AbortCommand",
"Esc": "AbortCommand",
// Mouse bindings
"MouseWheelUp": "HistoryUp",
"MouseWheelDown": "HistoryDown",
"MouseLeft": "MousePress",
"MouseMiddle": "PastePrimary",
}

View File

@@ -1,10 +1,17 @@
package action
import (
"github.com/zyedidia/tcell"
"bytes"
"errors"
"fmt"
"strings"
"github.com/zyedidia/tcell/v2"
)
type Event interface{}
type Event interface {
Name() string
}
// RawEvent is simply an escape code
// We allow users to directly bind escape codes
@@ -13,6 +20,10 @@ type RawEvent struct {
esc string
}
func (r RawEvent) Name() string {
return r.esc
}
// KeyEvent is a key event containing a key code,
// some possible modifiers (alt, ctrl, etc...) and
// a rune if it was simply a character press
@@ -22,6 +33,71 @@ type KeyEvent struct {
code tcell.Key
mod tcell.ModMask
r rune
any bool
}
func metaToAlt(mod tcell.ModMask) tcell.ModMask {
if mod&tcell.ModMeta != 0 {
mod &= ^tcell.ModMeta
mod |= tcell.ModAlt
}
return mod
}
func (k KeyEvent) Name() string {
if k.any {
return "<any>"
}
s := ""
m := []string{}
if k.mod&tcell.ModShift != 0 {
m = append(m, "Shift")
}
if k.mod&tcell.ModAlt != 0 {
m = append(m, "Alt")
}
if k.mod&tcell.ModMeta != 0 {
m = append(m, "Meta")
}
if k.mod&tcell.ModCtrl != 0 {
m = append(m, "Ctrl")
}
ok := false
if s, ok = tcell.KeyNames[k.code]; !ok {
if k.code == tcell.KeyRune {
s = string(k.r)
} else {
s = fmt.Sprintf("Key[%d,%d]", k.code, int(k.r))
}
}
if len(m) != 0 {
if k.mod&tcell.ModCtrl != 0 && strings.HasPrefix(s, "Ctrl-") {
s = s[5:]
if len(s) == 1 {
s = strings.ToLower(s)
}
}
return fmt.Sprintf("%s-%s", strings.Join(m, "-"), s)
}
return s
}
// A KeySequence defines a list of consecutive
// events. All events in the sequence must be KeyEvents
// or MouseEvents.
type KeySequenceEvent struct {
keys []Event
}
func (k KeySequenceEvent) Name() string {
buf := bytes.Buffer{}
for _, e := range k.keys {
buf.WriteByte('<')
buf.WriteString(e.Name())
buf.WriteByte('>')
}
return buf.String()
}
// MouseEvent is a mouse event with a mouse button and
@@ -31,8 +107,54 @@ type MouseEvent struct {
mod tcell.ModMask
}
type KeyAction func(Handler) bool
type MouseAction func(Handler, tcell.EventMouse) bool
func (m MouseEvent) Name() string {
mod := ""
if m.mod&tcell.ModShift != 0 {
mod = "Shift-"
}
if m.mod&tcell.ModAlt != 0 {
mod = "Alt-"
}
if m.mod&tcell.ModMeta != 0 {
mod = "Meta-"
}
if m.mod&tcell.ModCtrl != 0 {
mod = "Ctrl-"
}
for k, v := range mouseEvents {
if v == m.btn {
return fmt.Sprintf("%s%s", mod, k)
}
}
return ""
}
// ConstructEvent takes a tcell event and returns a micro
// event. Note that tcell events can't express certain
// micro events such as key sequences. This function is
// mostly used for debugging/raw panes or constructing
// intermediate micro events while parsing a sequence.
func ConstructEvent(event tcell.Event) (Event, error) {
switch e := event.(type) {
case *tcell.EventKey:
return KeyEvent{
code: e.Key(),
mod: metaToAlt(e.Modifiers()),
r: e.Rune(),
}, nil
case *tcell.EventRaw:
return RawEvent{
esc: e.EscSeq(),
}, nil
case *tcell.EventMouse:
return MouseEvent{
btn: e.Buttons(),
mod: metaToAlt(e.Modifiers()),
}, nil
}
return nil, errors.New("No micro event equivalent")
}
// A Handler will take a tcell event and execute it
// appropriately

View File

@@ -186,6 +186,16 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
if strings.HasPrefix("doas", input) {
suggestions = append(suggestions, "doas")
}
case "clipboard":
if strings.HasPrefix("external", input) {
suggestions = append(suggestions, "external")
}
if strings.HasPrefix("internal", input) {
suggestions = append(suggestions, "internal")
}
if strings.HasPrefix("terminal", input) {
suggestions = append(suggestions, "terminal")
}
}
}
sort.Strings(suggestions)

View File

@@ -2,17 +2,57 @@ package action
import (
"bytes"
"strings"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/display"
"github.com/zyedidia/micro/v2/internal/info"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
type InfoKeyAction func(*InfoPane)
var InfoBindings *KeyTree
var InfoBufBindings *KeyTree
func init() {
InfoBindings = NewKeyTree()
InfoBufBindings = NewKeyTree()
}
func InfoMapEvent(k Event, action string) {
switch e := k.(type) {
case KeyEvent, KeySequenceEvent, RawEvent:
infoMapKey(e, action)
case MouseEvent:
infoMapMouse(e, action)
}
}
func infoMapKey(k Event, action string) {
if f, ok := InfoKeyActions[action]; ok {
InfoBindings.RegisterKeyBinding(k, InfoKeyActionGeneral(f))
} else if f, ok := BufKeyActions[action]; ok {
InfoBufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(f))
}
}
func infoMapMouse(k MouseEvent, action string) {
// TODO: map mouse
if f, ok := BufMouseActions[action]; ok {
InfoBufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f))
} else {
infoMapKey(k, action)
}
}
func InfoKeyActionGeneral(a InfoKeyAction) PaneKeyAction {
return func(p Pane) bool {
a(p.(*InfoPane))
return true
}
}
type InfoPane struct {
*BufPane
*info.InfoBuf
@@ -22,6 +62,7 @@ func NewInfoPane(ib *info.InfoBuf, w display.BWindow, tab *Tab) *InfoPane {
ip := new(InfoPane)
ip.InfoBuf = ib
ip.BufPane = NewBufPane(ib.Buffer, w, tab)
ip.BufPane.bindings = InfoBufBindings
return ip
}
@@ -42,7 +83,7 @@ func (h *InfoPane) HandleEvent(event tcell.Event) {
case *tcell.EventKey:
ke := KeyEvent{
code: e.Key(),
mod: e.Modifiers(),
mod: metaToAlt(e.Modifiers()),
r: e.Rune(),
}
@@ -76,104 +117,43 @@ func (h *InfoPane) HandleEvent(event tcell.Event) {
// DoKeyEvent executes a key event for the command bar, doing any overridden actions
func (h *InfoPane) DoKeyEvent(e KeyEvent) bool {
done := false
if action, ok := BufKeyBindings[e]; ok {
estr := BufKeyStrings[e]
for _, s := range InfoNones {
if s == estr {
return false
}
}
for s, a := range InfoOverrides {
// TODO this is a hack and really we should have support
// for having binding overrides for different buffers
if strings.HasPrefix(estr, s) {
done = true
a(h)
break
}
}
if !done {
done = action(h.BufPane)
action, more := InfoBindings.NextEvent(e, nil)
if action != nil && !more {
action(h)
InfoBindings.ResetEvents()
return true
} else if action == nil && !more {
InfoBindings.ResetEvents()
// return false //TODO:?
}
if !more {
action, more = InfoBufBindings.NextEvent(e, nil)
if action != nil && !more {
done := action(h.BufPane)
InfoBufBindings.ResetEvents()
return done
} else if action == nil && !more {
InfoBufBindings.ResetEvents()
}
}
return done
return more
}
// InfoNones is a list of actions that should have no effect when executed
// by an infohandler
var InfoNones = []string{
"Save",
"SaveAll",
"SaveAs",
"Find",
"FindNext",
"FindPrevious",
"Center",
"DuplicateLine",
"MoveLinesUp",
"MoveLinesDown",
"OpenFile",
"Start",
"End",
"PageUp",
"PageDown",
"SelectPageUp",
"SelectPageDown",
"HalfPageUp",
"HalfPageDown",
"ToggleHelp",
"ToggleKeyMenu",
"ToggleDiffGutter",
"ToggleRuler",
"JumpLine",
"ClearStatus",
"ShellMode",
"CommandMode",
"AddTab",
"PreviousTab",
"NextTab",
"NextSplit",
"PreviousSplit",
"Unsplit",
"VSplit",
"HSplit",
"ToggleMacro",
"PlayMacro",
"Suspend",
"ScrollUp",
"ScrollDown",
"SpawnMultiCursor",
"SpawnMultiCursorSelect",
"RemoveMultiCursor",
"RemoveAllMultiCursors",
"SkipMultiCursor",
}
// InfoOverrides is the list of actions which have been overridden
// by the infohandler
var InfoOverrides = map[string]InfoKeyAction{
"CursorUp": (*InfoPane).CursorUp,
"CursorDown": (*InfoPane).CursorDown,
"InsertNewline": (*InfoPane).InsertNewline,
"Autocomplete": (*InfoPane).Autocomplete,
"Escape": (*InfoPane).Escape,
"Quit": (*InfoPane).Quit,
"QuitAll": (*InfoPane).QuitAll,
}
// CursorUp cycles history up
func (h *InfoPane) CursorUp() {
// HistoryUp cycles history up
func (h *InfoPane) HistoryUp() {
h.UpHistory(h.History[h.PromptType])
}
// CursorDown cycles history down
func (h *InfoPane) CursorDown() {
// HistoryDown cycles history down
func (h *InfoPane) HistoryDown() {
h.DownHistory(h.History[h.PromptType])
}
// Autocomplete begins autocompletion
func (h *InfoPane) Autocomplete() {
func (h *InfoPane) CommandComplete() {
b := h.Buf
if b.HasSuggestions {
b.CycleAutocomplete(true)
@@ -201,24 +181,23 @@ func (h *InfoPane) Autocomplete() {
}
}
// InsertNewline completes the prompt
func (h *InfoPane) InsertNewline() {
// ExecuteCommand completes the prompt
func (h *InfoPane) ExecuteCommand() {
if !h.HasYN {
h.DonePrompt(false)
}
}
// Quit cancels the prompt
func (h *InfoPane) Quit() {
// AbortCommand cancels the prompt
func (h *InfoPane) AbortCommand() {
h.DonePrompt(true)
}
// QuitAll cancels the prompt
func (h *InfoPane) QuitAll() {
h.DonePrompt(true)
}
// Escape cancels the prompt
func (h *InfoPane) Escape() {
h.DonePrompt(true)
// InfoKeyActions contains the list of all possible key actions the infopane could execute
var InfoKeyActions = map[string]InfoKeyAction{
"HistoryUp": (*InfoPane).HistoryUp,
"HistoryDown": (*InfoPane).HistoryDown,
"CommandComplete": (*InfoPane).CommandComplete,
"ExecuteCommand": (*InfoPane).ExecuteCommand,
"AbortCommand": (*InfoPane).AbortCommand,
}

261
internal/action/keytree.go Normal file
View File

@@ -0,0 +1,261 @@
package action
import (
"bytes"
"github.com/zyedidia/tcell/v2"
)
type PaneKeyAction func(Pane) bool
type PaneMouseAction func(Pane, *tcell.EventMouse) bool
type PaneKeyAnyAction func(Pane, []KeyEvent) bool
// A KeyTreeNode stores a single node in the KeyTree (trie). The
// children are stored as a map, and any node may store a list of
// actions (the list will be nil if no actions correspond to a certain
// node)
type KeyTreeNode struct {
children map[Event]*KeyTreeNode
// Only one of these actions may be active in the current
// mode, and only one will be returned. If multiple actions
// are active, it is undefined which one will be the one
// returned.
actions []TreeAction
}
func NewKeyTreeNode() *KeyTreeNode {
n := new(KeyTreeNode)
n.children = make(map[Event]*KeyTreeNode)
n.actions = []TreeAction{}
return n
}
// A TreeAction stores an action, and a set of mode constraints for
// the action to be active.
type TreeAction struct {
// only one of these can be non-nil
action PaneKeyAction
any PaneKeyAnyAction
mouse PaneMouseAction
modes []ModeConstraint
}
// A KeyTree is a data structure for storing keybindings. It maps
// key events to actions, and maintains a set of currently enabled
// modes, which affects the action that is returned for a key event.
// The tree acts like a Trie for Events to handle sequence events.
type KeyTree struct {
root *KeyTreeNode
modes map[string]bool
cursor KeyTreeCursor
}
// A KeyTreeCursor keeps track of the current location within the
// tree, and stores any information from previous events that may
// be needed to execute the action (values of wildcard events or
// mouse events)
type KeyTreeCursor struct {
node *KeyTreeNode
recordedEvents []Event
wildcards []KeyEvent
mouseInfo *tcell.EventMouse
}
// MakeClosure uses the information stored in a key tree cursor to construct
// a PaneKeyAction from a TreeAction (which may have a PaneKeyAction, PaneMouseAction,
// or AnyAction)
func (k *KeyTreeCursor) MakeClosure(a TreeAction) PaneKeyAction {
if a.action != nil {
return a.action
} else if a.any != nil {
return func(p Pane) bool {
return a.any(p, k.wildcards)
}
} else if a.mouse != nil {
return func(p Pane) bool {
return a.mouse(p, k.mouseInfo)
}
}
return nil
}
// NewKeyTree allocates and returns an empty key tree
func NewKeyTree() *KeyTree {
root := NewKeyTreeNode()
tree := new(KeyTree)
tree.root = root
tree.modes = make(map[string]bool)
tree.cursor = KeyTreeCursor{
node: root,
wildcards: []KeyEvent{},
mouseInfo: nil,
}
return tree
}
// A ModeConstraint specifies that an action can only be executed
// while a certain mode is enabled or disabled.
type ModeConstraint struct {
mode string
disabled bool
}
// RegisterKeyBinding registers a PaneKeyAction with an Event.
func (k *KeyTree) RegisterKeyBinding(e Event, a PaneKeyAction) {
k.registerBinding(e, TreeAction{
action: a,
any: nil,
mouse: nil,
modes: nil,
})
}
// RegisterKeyAnyBinding registers a PaneKeyAnyAction with an Event.
// The event should contain an "any" event.
func (k *KeyTree) RegisterKeyAnyBinding(e Event, a PaneKeyAnyAction) {
k.registerBinding(e, TreeAction{
action: nil,
any: a,
mouse: nil,
modes: nil,
})
}
// RegisterMouseBinding registers a PaneMouseAction with an Event.
// The event should contain a mouse event.
func (k *KeyTree) RegisterMouseBinding(e Event, a PaneMouseAction) {
k.registerBinding(e, TreeAction{
action: nil,
any: nil,
mouse: a,
modes: nil,
})
}
func (k *KeyTree) registerBinding(e Event, a TreeAction) {
switch ev := e.(type) {
case KeyEvent, MouseEvent, RawEvent:
newNode, ok := k.root.children[e]
if !ok {
newNode = NewKeyTreeNode()
k.root.children[e] = newNode
}
// newNode.actions = append(newNode.actions, a)
newNode.actions = []TreeAction{a}
case KeySequenceEvent:
n := k.root
for _, key := range ev.keys {
newNode, ok := n.children[key]
if !ok {
newNode = NewKeyTreeNode()
n.children[key] = newNode
}
n = newNode
}
// n.actions = append(n.actions, a)
n.actions = []TreeAction{a}
}
}
// NextEvent returns the action for the current sequence where e is the next
// event. Even if the action was registered as a PaneKeyAnyAction or PaneMouseAction,
// it will be returned as a PaneKeyAction closure where the appropriate arguments
// have been provided.
// If no action is associated with the given Event, or mode constraints are not
// met for that action, nil is returned.
// A boolean is returned to indicate if there is a conflict with this action. A
// conflict occurs when there is an active action for this event but there are
// bindings associated with further sequences starting with this event. The
// calling function can decide what to do about the conflict (e.g. use a
// timeout).
func (k *KeyTree) NextEvent(e Event, mouse *tcell.EventMouse) (PaneKeyAction, bool) {
n := k.cursor.node
c, ok := n.children[e]
if !ok {
return nil, false
}
more := len(c.children) > 0
k.cursor.node = c
k.cursor.recordedEvents = append(k.cursor.recordedEvents, e)
switch ev := e.(type) {
case KeyEvent:
if ev.any {
k.cursor.wildcards = append(k.cursor.wildcards, ev)
}
case MouseEvent:
k.cursor.mouseInfo = mouse
}
if len(c.actions) > 0 {
// check if actions are active
for _, a := range c.actions {
active := true
for _, mc := range a.modes {
// if any mode constraint is not met, the action is not active
hasMode := k.modes[mc.mode]
if hasMode != mc.disabled {
active = false
}
}
if active {
// the first active action to be found is returned
return k.cursor.MakeClosure(a), more
}
}
}
return nil, more
}
// ResetEvents sets the current sequence back to the initial value.
func (k *KeyTree) ResetEvents() {
k.cursor.node = k.root
k.cursor.wildcards = []KeyEvent{}
k.cursor.recordedEvents = []Event{}
k.cursor.mouseInfo = nil
}
// CurrentEventsStr returns the list of recorded events as a string
func (k *KeyTree) RecordedEventsStr() string {
buf := &bytes.Buffer{}
for _, e := range k.cursor.recordedEvents {
buf.WriteString(e.Name())
}
return buf.String()
}
// DeleteBinding removes any currently active actions associated with the
// given event.
func (k *KeyTree) DeleteBinding(e Event) {
}
// DeleteAllBindings removes all actions associated with the given event,
// regardless of whether they are active or not.
func (k *KeyTree) DeleteAllBindings(e Event) {
}
// SetMode enables or disabled a given mode
func (k *KeyTree) SetMode(mode string, en bool) {
k.modes[mode] = en
}
// HasMode returns if the given mode is currently active
func (k *KeyTree) HasMode(mode string) bool {
return k.modes[mode]
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/display"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
type RawPane struct {
@@ -36,8 +36,8 @@ func (h *RawPane) HandleEvent(event tcell.Event) {
h.Buf.Insert(h.Cursor.Loc, reflect.TypeOf(event).String()[7:])
switch e := event.(type) {
case *tcell.EventKey:
e, err := ConstructEvent(event)
if err == nil {
h.Buf.Insert(h.Cursor.Loc, fmt.Sprintf(": %s", e.Name()))
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/zyedidia/micro/v2/internal/display"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/views"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
// The TabList is a list of tabs and a window to display the tab bar
@@ -175,6 +175,7 @@ func NewTabFromBuffer(x, y, width, height int, b *buffer.Buffer) *Tab {
t := new(Tab)
t.Node = views.NewRoot(x, y, width, height)
t.UIWindow = display.NewUIWindow(t.Node)
t.release = true
e := NewBufPaneFromBuf(b, t)
e.SetID(t.ID())
@@ -187,6 +188,7 @@ func NewTabFromPane(x, y, width, height int, pane Pane) *Tab {
t := new(Tab)
t.Node = views.NewRoot(x, y, width, height)
t.UIWindow = display.NewUIWindow(t.Node)
t.release = true
pane.SetTab(t)
pane.SetID(t.ID())
@@ -220,9 +222,8 @@ func (t *Tab) HandleEvent(event tcell.Event) {
}
if wasReleased {
resizeID := t.GetMouseSplitID(buffer.Loc{mx, my})
if resizeID != 0 {
t.resizing = t.GetNode(uint64(resizeID))
t.resizing = t.GetMouseSplitNode(buffer.Loc{mx, my})
if t.resizing != nil {
return
}

View File

@@ -24,7 +24,10 @@ func RunTermEmulator(h *BufPane, input string, wait bool, getOutput bool, callba
}
t := new(shell.Terminal)
t.Start(args, getOutput, wait, callback, userargs)
err = t.Start(args, getOutput, wait, callback, userargs)
if err != nil {
return err
}
h.AddTab()
id := MainTab().Panes[0].ID()

View File

@@ -4,14 +4,49 @@ import (
"errors"
"runtime"
"github.com/zyedidia/clipboard"
"github.com/zyedidia/micro/v2/internal/clipboard"
"github.com/zyedidia/micro/v2/internal/display"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
"github.com/zyedidia/terminal"
)
type TermKeyAction func(*TermPane)
var TermBindings *KeyTree
func init() {
TermBindings = NewKeyTree()
}
func TermKeyActionGeneral(a TermKeyAction) PaneKeyAction {
return func(p Pane) bool {
a(p.(*TermPane))
return true
}
}
func TermMapEvent(k Event, action string) {
switch e := k.(type) {
case KeyEvent, KeySequenceEvent, RawEvent:
termMapKey(e, action)
case MouseEvent:
termMapMouse(e, action)
}
}
func termMapKey(k Event, action string) {
if f, ok := TermKeyActions[action]; ok {
TermBindings.RegisterKeyBinding(k, TermKeyActionGeneral(f))
}
}
func termMapMouse(k MouseEvent, action string) {
// TODO: map mouse
termMapKey(k, action)
}
type TermPane struct {
*shell.Terminal
display.Window
@@ -53,6 +88,7 @@ func (t *TermPane) Tab() *Tab {
func (t *TermPane) Close() {}
// Quit closes this termpane
func (t *TermPane) Quit() {
t.Close()
if len(MainTab().Panes) > 1 {
@@ -66,6 +102,7 @@ func (t *TermPane) Quit() {
}
}
// Unsplit removes this split
func (t *TermPane) Unsplit() {
n := MainTab().GetNode(t.id)
n.Unsplit()
@@ -81,6 +118,26 @@ func (t *TermPane) Unsplit() {
// copy-paste
func (t *TermPane) HandleEvent(event tcell.Event) {
if e, ok := event.(*tcell.EventKey); ok {
ke := KeyEvent{
code: e.Key(),
mod: metaToAlt(e.Modifiers()),
r: e.Rune(),
}
action, more := TermBindings.NextEvent(ke, nil)
if !more {
if action != nil {
action(t)
TermBindings.ResetEvents()
return
}
TermBindings.ResetEvents()
}
if more {
return
}
if t.Status == shell.TTDone {
switch e.Key() {
case tcell.KeyEscape, tcell.KeyCtrlQ, tcell.KeyEnter:
@@ -90,7 +147,7 @@ func (t *TermPane) HandleEvent(event tcell.Event) {
}
}
if e.Key() == tcell.KeyCtrlC && t.HasSelection() {
clipboard.WriteAll(t.GetSelection(t.GetView().Width), "clipboard")
clipboard.Write(t.GetSelection(t.GetView().Width), clipboard.ClipboardReg)
InfoBar.Message("Copied selection to clipboard")
} else if t.Status != shell.TTDone {
t.WriteString(event.EscSeq())
@@ -134,6 +191,41 @@ func (t *TermPane) HandleEvent(event tcell.Event) {
}
}
// Exit closes the termpane
func (t *TermPane) Exit() {
t.Terminal.Close()
t.Quit()
}
// CommandMode opens the termpane's command mode
func (t *TermPane) CommandMode() {
InfoBar.Prompt("> ", "", "TerminalCommand", nil, func(resp string, canceled bool) {
if !canceled {
t.HandleCommand(resp)
}
})
}
// NextSplit moves to the next split
func (t *TermPane) NextSplit() {
a := t.tab.active
if a < len(t.tab.Panes)-1 {
a++
} else {
a = 0
}
t.tab.SetActive(a)
}
// HandleCommand handles a command for the term pane
func (t *TermPane) HandleCommand(input string) {
InfoBar.Error("Commands are unsupported in term for now")
}
// TermKeyActions contains the list of all possible key actions the termpane could execute
var TermKeyActions = map[string]TermKeyAction{
"Exit": (*TermPane).Exit,
"CommandMode": (*TermPane).CommandMode,
"NextSplit": (*TermPane).NextSplit,
}

View File

@@ -5,6 +5,7 @@ import (
"io"
"os"
"path/filepath"
"sync/atomic"
"time"
"github.com/zyedidia/micro/v2/internal/config"
@@ -36,7 +37,10 @@ func backupThread() {
for len(backupRequestChan) > 0 {
b := <-backupRequestChan
b.Backup()
bfini := atomic.LoadInt32(&(b.fini)) != 0
if !bfini {
b.Backup()
}
}
}
}

View File

@@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
luar "layeh.com/gopher-luar"
@@ -184,6 +185,7 @@ type Buffer struct {
*EventHandler
*SharedBuffer
fini int32
cursors []*Cursor
curCursor int
StartCursor Loc
@@ -396,6 +398,8 @@ func (b *Buffer) Fini() {
if b.Type == BTStdout {
fmt.Fprint(util.Stdout, string(b.Bytes()))
}
atomic.StoreInt32(&(b.fini), int32(1))
}
// GetName returns the name that should be displayed in the statusline

View File

@@ -1,7 +1,7 @@
package buffer
import (
"github.com/zyedidia/clipboard"
"github.com/zyedidia/micro/v2/internal/clipboard"
"github.com/zyedidia/micro/v2/internal/util"
)
@@ -125,10 +125,10 @@ func (c *Cursor) End() {
// CopySelection copies the user's selection to either "primary"
// or "clipboard"
func (c *Cursor) CopySelection(target string) {
func (c *Cursor) CopySelection(target clipboard.Register) {
if c.HasSelection() {
if target != "primary" || c.buf.Settings["useprimary"].(bool) {
clipboard.WriteAll(string(c.GetSelection()), target)
if target != clipboard.PrimaryReg || c.buf.Settings["useprimary"].(bool) {
clipboard.WriteMulti(string(c.GetSelection()), target, c.Num, c.buf.NumCursors())
}
}
}

View File

@@ -2,7 +2,7 @@ package buffer
import (
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
type MsgType int

View File

@@ -10,9 +10,11 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error {
if option == "fastdirty" {
if !nativeValue.(bool) {
e := calcHash(b, &b.origHash)
if e == ErrFileTooLarge {
b.Settings["fastdirty"] = false
if !b.Modified() {
e := calcHash(b, &b.origHash)
if e == ErrFileTooLarge {
b.Settings["fastdirty"] = false
}
}
}
} else if option == "statusline" {

View File

@@ -0,0 +1,151 @@
package clipboard
import (
"errors"
"github.com/zyedidia/clipboard"
)
type Method int
const (
// External relies on external tools for accessing the clipboard
// These include xclip, xsel, wl-clipboard for linux, pbcopy/pbpaste on Mac,
// and Syscalls on Windows.
External Method = iota
// Terminal uses the terminal to manage the clipboard via OSC 52. Many
// terminals do not support OSC 52, in which case this method won't work.
Terminal
// Internal just manages the clipboard with an internal buffer and doesn't
// attempt to interface with the system clipboard
Internal
)
// CurrentMethod is the method used to store clipboard information
var CurrentMethod Method = Internal
// A Register is a buffer used to store text. The system clipboard has the 'clipboard'
// and 'primary' (linux-only) registers, but other registers may be used internal to micro.
type Register int
const (
// ClipboardReg is the main system clipboard
ClipboardReg Register = -1
// PrimaryReg is the system primary clipboard (linux only)
PrimaryReg = -2
)
// Initialize attempts to initialize the clipboard using the given method
func Initialize(m Method) error {
var err error
switch m {
case External:
err = clipboard.Initialize()
}
return err
}
// SetMethod changes the clipboard access method
func SetMethod(m string) Method {
switch m {
case "internal":
CurrentMethod = Internal
case "external":
CurrentMethod = External
case "terminal":
CurrentMethod = Terminal
}
return CurrentMethod
}
// Read reads from a clipboard register
func Read(r Register) (string, error) {
return read(r, CurrentMethod)
}
// Write writes text to a clipboard register
func Write(text string, r Register) error {
return write(text, r, CurrentMethod)
}
// ReadMulti reads text from a clipboard register for a certain multi-cursor
func ReadMulti(r Register, num, ncursors int) (string, error) {
clip, err := Read(r)
if err != nil {
return "", err
}
if ValidMulti(r, clip, ncursors) {
return multi.getText(r, num), nil
}
return clip, nil
}
// WriteMulti writes text to a clipboard register for a certain multi-cursor
func WriteMulti(text string, r Register, num int, ncursors int) error {
return writeMulti(text, r, num, ncursors, CurrentMethod)
}
// ValidMulti checks if the internal multi-clipboard is valid and up-to-date
// with the system clipboard
func ValidMulti(r Register, clip string, ncursors int) bool {
return multi.isValid(r, clip, ncursors)
}
func writeMulti(text string, r Register, num int, ncursors int, m Method) error {
multi.writeText(text, r, num, ncursors)
return write(multi.getAllText(r), r, m)
}
func read(r Register, m Method) (string, error) {
switch m {
case External:
switch r {
case ClipboardReg:
return clipboard.ReadAll("clipboard")
case PrimaryReg:
return clipboard.ReadAll("primary")
default:
return internal.read(r), nil
}
case Internal:
return internal.read(r), nil
case Terminal:
switch r {
case ClipboardReg:
// terminal paste works by sending an esc sequence to the
// terminal to trigger a paste event
return terminal.read("clipboard")
case PrimaryReg:
return terminal.read("primary")
default:
return internal.read(r), nil
}
}
return "", errors.New("Invalid clipboard method")
}
func write(text string, r Register, m Method) error {
switch m {
case External:
switch r {
case ClipboardReg:
return clipboard.WriteAll(text, "clipboard")
case PrimaryReg:
return clipboard.WriteAll(text, "primary")
default:
internal.write(text, r)
}
case Internal:
internal.write(text, r)
case Terminal:
switch r {
case ClipboardReg:
return terminal.write(text, "c")
case PrimaryReg:
return terminal.write(text, "p")
default:
internal.write(text, r)
}
}
return nil
}

View File

@@ -0,0 +1,17 @@
package clipboard
type internalClipboard map[Register]string
var internal internalClipboard
func init() {
internal = make(internalClipboard)
}
func (c internalClipboard) read(r Register) string {
return c[r]
}
func (c internalClipboard) write(text string, r Register) {
c[r] = text
}

View File

@@ -0,0 +1,63 @@
package clipboard
import (
"bytes"
)
// For storing multi cursor clipboard contents
type multiClipboard map[Register][]string
var multi multiClipboard
func (c multiClipboard) getAllText(r Register) string {
content := c[r]
if content == nil {
return ""
}
buf := &bytes.Buffer{}
for _, s := range content {
buf.WriteString(s)
}
return buf.String()
}
func (c multiClipboard) getText(r Register, num int) string {
content := c[r]
if content == nil || len(content) <= num {
return ""
}
return content[num]
}
// isValid checks if the text stored in this multi-clipboard is the same as the
// text stored in the system clipboard (provided as an argument), and therefore
// if it is safe to use the multi-clipboard for pasting instead of the system
// clipboard.
func (c multiClipboard) isValid(r Register, clipboard string, ncursors int) bool {
content := c[r]
if content == nil || len(content) != ncursors {
return false
}
return clipboard == c.getAllText(r)
}
func (c multiClipboard) writeText(text string, r Register, num int, ncursors int) {
content := c[r]
if content == nil || len(content) != ncursors {
content = make([]string, ncursors, ncursors)
c[r] = content
}
if num >= ncursors {
return
}
content[num] = text
}
func init() {
multi = make(multiClipboard)
}

View File

@@ -0,0 +1,33 @@
package clipboard
import (
"errors"
"time"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/tcell/v2"
)
type terminalClipboard struct{}
var terminal terminalClipboard
func (t terminalClipboard) read(reg string) (string, error) {
screen.Screen.GetClipboard(reg)
// wait at most 200ms for response
for {
select {
case event := <-screen.Events:
e, ok := event.(*tcell.EventPaste)
if ok {
return e.Text(), nil
}
case <-time.After(200 * time.Millisecond):
return "", errors.New("No clipboard received from terminal")
}
}
}
func (t terminalClipboard) write(text, reg string) error {
return screen.Screen.SetClipboard(text, reg)
}

View File

@@ -6,7 +6,7 @@ import (
"strconv"
"strings"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
// Micro's default style
@@ -117,16 +117,12 @@ func ParseColorscheme(text string) (map[string]tcell.Style, error) {
// StringToStyle returns a style from a string
// The strings must be in the format "extra foregroundcolor,backgroundcolor"
// The 'extra' can be bold, reverse, or underline
// The 'extra' can be bold, reverse, italic or underline
func StringToStyle(str string) tcell.Style {
var fg, bg string
spaceSplit := strings.Split(str, " ")
var split []string
if len(spaceSplit) > 1 {
split = strings.Split(spaceSplit[1], ",")
} else {
split = strings.Split(str, ",")
}
split = strings.Split(spaceSplit[len(spaceSplit)-1], ",")
if len(split) > 1 {
fg, bg = split[0], split[1]
} else {

View File

@@ -4,7 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
func TestSimpleStringToStyle(t *testing.T) {
@@ -26,6 +26,18 @@ func TestAttributeStringToStyle(t *testing.T) {
assert.NotEqual(t, 0, attr&tcell.AttrBold)
}
func TestMultiAttributesStringToStyle(t *testing.T) {
s := StringToStyle("bold italic underline cyan,brightcyan")
fg, bg, attr := s.Decompose()
assert.Equal(t, tcell.ColorTeal, fg)
assert.Equal(t, tcell.ColorAqua, bg)
assert.NotEqual(t, 0, attr&tcell.AttrBold)
assert.NotEqual(t, 0, attr&tcell.AttrItalic)
assert.NotEqual(t, 0, attr&tcell.AttrUnderline)
}
func TestColor256StringToStyle(t *testing.T) {
s := StringToStyle("128,60")

View File

@@ -5,3 +5,7 @@ const (
)
var Bindings map[string]string
func init() {
Bindings = make(map[string]string)
}

View File

@@ -607,7 +607,7 @@ func UpdatePlugins(out io.Writer, plugins []string) {
// if no plugins are specified, update all installed plugins.
if len(plugins) == 0 {
for _, p := range Plugins {
if !p.IsEnabled() {
if !p.IsEnabled() || p.Default {
continue
}
plugins = append(plugins, p.Name)

File diff suppressed because one or more lines are too long

View File

@@ -43,6 +43,7 @@ func init() {
// Options with validators
var optionValidators = map[string]optionValidator{
"autosave": validateNonNegativeValue,
"clipboard": validateClipboard,
"tabsize": validatePositiveValue,
"scrollmargin": validateNonNegativeValue,
"scrollspeed": validateNonNegativeValue,
@@ -322,6 +323,7 @@ func DefaultCommonSettings() map[string]interface{} {
// default values
var DefaultGlobalOnlySettings = map[string]interface{}{
"autosave": float64(0),
"clipboard": "external",
"colorscheme": "default",
"divchars": "|-",
"divreverse": true,
@@ -450,6 +452,22 @@ func validateColorscheme(option string, value interface{}) error {
return nil
}
func validateClipboard(option string, value interface{}) error {
val, ok := value.(string)
if !ok {
return errors.New("Expected string type for clipboard")
}
switch val {
case "internal", "external", "terminal":
default:
return errors.New(option + " must be 'internal', 'external', or 'terminal'")
}
return nil
}
func validateLineEnding(option string, value interface{}) error {
endingType, ok := value.(string)

View File

@@ -8,7 +8,7 @@ import (
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
// The BufWindow provides a way of displaying a certain section
@@ -736,8 +736,14 @@ func (w *BufWindow) displayScrollBar() {
barsize = 1
}
barstart := w.Y + int(float64(w.StartLine)/float64(w.Buf.LinesNum())*float64(w.Height))
scrollBarStyle := config.DefStyle.Reverse(true)
if style, ok := config.Colorscheme["scrollbar"]; ok {
scrollBarStyle = style
}
for y := barstart; y < util.Min(barstart+barsize, w.Y+bufHeight); y++ {
screen.SetContent(scrollX, y, '|', nil, config.DefStyle.Reverse(true))
screen.SetContent(scrollX, y, '|', nil, scrollBarStyle)
}
}
}

View File

@@ -7,7 +7,7 @@ import (
"github.com/zyedidia/micro/v2/internal/info"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
type InfoWindow struct {

View File

@@ -6,7 +6,7 @@ import (
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/shell"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
"github.com/zyedidia/terminal"
)

View File

@@ -38,15 +38,14 @@ func (w *UIWindow) drawNode(n *views.Node) {
}
for i, c := range cs {
if c.IsLeaf() && c.Kind == views.STVert {
if c.Kind == views.STVert {
if i != len(cs)-1 {
for h := 0; h < c.H; h++ {
screen.SetContent(c.X+c.W, c.Y+h, divchar, combc, dividerStyle)
}
}
} else {
w.drawNode(c)
}
w.drawNode(c)
}
}
@@ -54,32 +53,32 @@ func (w *UIWindow) Display() {
w.drawNode(w.root)
}
func (w *UIWindow) GetMouseSplitID(vloc buffer.Loc) uint64 {
var mouseLoc func(*views.Node) uint64
mouseLoc = func(n *views.Node) uint64 {
func (w *UIWindow) GetMouseSplitNode(vloc buffer.Loc) *views.Node {
var mouseLoc func(*views.Node) *views.Node
mouseLoc = func(n *views.Node) *views.Node {
cs := n.Children()
for i, c := range cs {
if c.Kind == views.STVert {
if i != len(cs)-1 {
if vloc.X == c.X+c.W && vloc.Y >= c.Y && vloc.Y < c.Y+c.H {
return c.ID()
return c
}
}
} else if c.Kind == views.STHoriz {
if i != len(cs)-1 {
if vloc.Y == c.Y+c.H-1 && vloc.X >= c.X && vloc.X < c.X+c.W {
return c.ID()
return c
}
}
}
}
for _, c := range cs {
m := mouseLoc(c)
if m != 0 {
if m != nil {
return m
}
}
return 0
return nil
}
return mouseLoc(w.root)
}

View File

@@ -61,6 +61,30 @@ func (i *InfoBuf) SaveHistory() {
}
}
// AddToHistory adds a new item to the history for the prompt type `ptype`.
// This function is not used by micro itself. It is useful for plugins
// which add their own items to the history, bypassing the infobar command line.
func (i *InfoBuf) AddToHistory(ptype string, item string) {
if i.HasPrompt && i.PromptType == ptype {
return
}
if _, ok := i.History[ptype]; !ok {
i.History[ptype] = []string{item}
} else {
i.History[ptype] = append(i.History[ptype], item)
// avoid duplicates
h := i.History[ptype]
for j := len(h) - 2; j >= 0; j-- {
if h[j] == h[len(h)-1] {
i.History[ptype] = append(h[:j], h[j+1:]...)
break
}
}
}
}
// UpHistory fetches the previous item in the history
func (i *InfoBuf) UpHistory(history []string) {
if i.HistoryNum > 0 && i.HasPrompt && !i.HasYN {

View File

@@ -145,6 +145,14 @@ func (i *InfoBuf) DonePrompt(canceled bool) {
i.PromptCallback(resp, false)
h := i.History[i.PromptType]
h[len(h)-1] = resp
// avoid duplicates
for j := len(h) - 2; j >= 0; j-- {
if h[j] == h[len(h)-1] {
i.History[i.PromptType] = append(h[:j], h[j+1:]...)
break
}
}
}
// i.PromptCallback = nil
}

View File

@@ -15,6 +15,7 @@ import (
"regexp"
"runtime"
"strings"
"sync"
"time"
"unicode/utf8"
@@ -24,6 +25,7 @@ import (
)
var L *lua.LState
var Lock sync.Mutex
// LoadFile loads a lua file
func LoadFile(module string, file string, data []byte) error {
@@ -565,8 +567,6 @@ func importHumanize() *lua.LTable {
L.SetField(pkg, "Bytes", luar.New(L, humanize.Bytes))
L.SetField(pkg, "Ordinal", luar.New(L, humanize.Ordinal))
L.SetField(pkg, "Ftoa", luar.New(L, humanize.Ftoa))
L.SetField(pkg, "FtoaWithDigits", luar.New(L, humanize.FtoaWithDigits))
return pkg
}

View File

@@ -4,11 +4,10 @@ import (
"errors"
"os"
"sync"
"unicode"
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/util"
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/v2"
)
// Screen is the tcell screen we use to draw to the terminal
@@ -19,6 +18,9 @@ import (
// same time too.
var Screen tcell.Screen
// Events is the channel of tcell events
var Events chan (tcell.Event)
// The lock is necessary since the screen is polled on a separate thread
var lock sync.Mutex
@@ -98,7 +100,7 @@ func ShowCursor(x, y int) {
// SetContent sets a cell at a point on the screen and makes sure that it is
// synced with the last cursor location
func SetContent(x, y int, mainc rune, combc []rune, style tcell.Style) {
if !unicode.IsPrint(mainc) {
if !Screen.CanDisplay(mainc, true) {
mainc = '<27>'
}
@@ -157,6 +159,8 @@ func Init() error {
return err
}
Screen.SetPaste(config.GetGlobalOption("paste").(bool))
// restore TERM
if config.GetGlobalOption("xterm").(bool) {
os.Setenv("TERM", oldTerm)

View File

@@ -37,6 +37,12 @@ type CallbackFile struct {
args []interface{}
}
// Job stores the executing command for the job, and the stdin pipe
type Job struct {
*exec.Cmd
Stdin io.WriteCloser
}
func (f *CallbackFile) Write(data []byte) (int, error) {
// This is either stderr or stdout
// In either case we create a new job function callback and put it in the jobs channel
@@ -47,13 +53,13 @@ func (f *CallbackFile) Write(data []byte) (int, error) {
// JobStart starts a shell command in the background with the given callbacks
// It returns an *exec.Cmd as the job id
func JobStart(cmd string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *exec.Cmd {
func JobStart(cmd string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *Job {
return JobSpawn("sh", []string{"-c", cmd}, onStdout, onStderr, onExit, userargs...)
}
// JobSpawn starts a process with args in the background with the given callbacks
// It returns an *exec.Cmd as the job id
func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *exec.Cmd {
func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *Job {
// Set up everything correctly if the functions have been provided
proc := exec.Command(cmdName, cmdArgs...)
var outbuf bytes.Buffer
@@ -67,6 +73,7 @@ func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit func(
} else {
proc.Stderr = &outbuf
}
stdin, _ := proc.StdinPipe()
go func() {
// Run the process in the background and create the onExit callback
@@ -75,20 +82,15 @@ func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit func(
Jobs <- jobFunc
}()
return proc
return &Job{proc, stdin}
}
// JobStop kills a job
func JobStop(cmd *exec.Cmd) {
cmd.Process.Kill()
func JobStop(j *Job) {
j.Process.Kill()
}
// JobSend sends the given data into the job's stdin stream
func JobSend(cmd *exec.Cmd, data string) {
stdin, err := cmd.StdinPipe()
if err != nil {
return
}
stdin.Write([]byte(data))
func JobSend(j *Job, data string) {
j.Stdin.Write([]byte(data))
}

View File

@@ -336,6 +336,10 @@ func GetModTime(path string) (time.Time, error) {
// EscapePath replaces every path separator in a given path with a %
func EscapePath(path string) string {
path = filepath.ToSlash(path)
if runtime.GOOS == "windows" {
// ':' is not valid in a path name on Windows but is ok on Unix
path = strings.Replace(path, ":", "%", -1)
}
return strings.Replace(path, "/", "%", -1)
}

View File

@@ -3,6 +3,7 @@ color-link comment "#bc9458,#2b2b2b"
color-link statement "#cc7833,#2b2b2b"
color-link constant "#a5c261,#2b2b2b"
color-link constant.bool "#6d9cbe,#2b2b2b"
color-link constant.specialChar "#459231,#2b2b2b"
color-link type "#6d9cbe,#2b2b2b"
color-link preproc "#cc7833,#2b2b2b"
color-link special "#cc7833,#2b2b2b"
@@ -18,6 +19,8 @@ color-link diff-modified "#FFAF00"
color-link diff-deleted "#D70000"
color-link gutter-warning "#a5c261,#11151C"
color-link symbol "#edb753,#2b2b2b"
color-link symbol.operator "#cc7833,#2b2b2b"
color-link symbol.brackets "#cc7833,#2b2b2b"
color-link identifier "#edb753,#2b2b2b"
color-link statusline "#b1b1b1,#232323"
color-link tabbar "bold #b1b1b1,#232323"

View File

@@ -4,8 +4,52 @@ because there are multiple methods. This help document will explain
the various methods for copying and pasting, how they work,
and the best methods for doing so over SSH.
# OSC 52 (terminal clipboard)
If possible, setting the `clipboard` option to `terminal` will give
best results because it will work over SSH and locally. However, there
is limited support among terminal emulators for the terminal clipboard
(which uses the OSC 52 protocol to communicate clipboard contents).
Here is a list of terminal emulators and their status:
* Kitty: supported, but only writing is enabled by default. To enable
reading, add `read-primary` and `read-clipboard` to the
`clipboard_control` option.
* iTerm2: only copying (writing to clipboard) is supported. Must be enabled in
`Preferences->General-> Selection->Applications in terminal may access clipboard`.
You can use Command-v to paste.
* `st`: supported.
* `rxvt-unicode`: not natively supported, but there is a Perl extension
[here](http://anti.teamidiot.de/static/nei/*/Code/urxvt/).
* `xterm`: supported, but disabled by default. It can be enabled by putting
the following in `.Xresources` or `.Xdefaults`:
`XTerm*disallowedWindowOps: 20,21,SetXprop`.
* `gnome-terminal`: does not support OSC 52.
**Summary:** If you want copy and paste to work over SSH, then you
should set `clipboard` to `terminal`, and make sure your terminal
supports OSC 52.
# Pasting
## Recommendations (TL;DR)
The recommended method of pasting is the following:
* If you are not working over SSH, use the micro keybinding (Ctrl-v
by default) to perform pastes. If on Linux, install `xclip` or
`xsel` beforehand.
* If you are working over SSH, use the terminal keybinding
(Ctrl-Shift-v or Command-v) to perform pastes. If your terminal
does not support bracketed paste, when performing a paste first
enable the `paste` option, and when finished disable the option.
## Micro paste events
Micro is an application that runs within the terminal. This means
@@ -56,20 +100,23 @@ machine's clipboard. On the other hand, the terminal keybinding
for paste will access your local clipboard and send the text over
the network as a paste event, which is what you want.
## Recommendations
# Copying
The recommended method of pasting is the following:
# Recommendations (TL;DR)
* If you are not working over SSH, use the micro keybinding (Ctrl-v
by default) to perform pastes. If on Linux, install `xclip` or
`xsel` beforehand.
The recommended method of copying is the following:
* If you are not working over SSH, use the micro keybinding (Ctrl-c by
default) to perform copies. If on Linux, install `xclip` or `xsel`
beforehand.
* If you are working over SSH, use the terminal keybinding
(Ctrl-Shift-v or Command-v) to perform pastes. If your terminal
does not support bracketed paste, when performing a paste first
enable the `paste` option, and when finished disable the option.
# Copying
(Ctrl-Shift-c or Command-c) to perform copies. You must first disable
the `mouse` option to perform a terminal selection, and you may wish
to disable line numbers and diff indicators (`ruler` and `diffgutter`
options) and close other splits. This method will only be able to copy
characters that are displayed on the screen (you will not be able to
copy more than one page's worth of characters).
Copying follows a similar discussion to the one above about pasting.
The primary difference is before performing a copy, the application
@@ -92,19 +139,3 @@ means that for copying multiple lines using the terminal selection, you
should first disable line numbers and diff indicators (turn off the `ruler`
and `diffgutter` options), otherwise they might be part of your selection
and copied.
## Recommendations
The recommended method of copying is the following:
* If you are not working over SSH, use the micro keybinding (Ctrl-c by
default) to perform copies. If on Linux, install `xclip` or `xsel`
beforehand.
* If you are working over SSH, use the terminal keybinding
(Ctrl-Shift-c or Command-c) to perform copies. You must first disable
the `mouse` option to perform a terminal selection, and you may wish
to disable line numbers and diff indicators (`ruler` and `diffgutter`
options) and close other splits. This method will only be able to copy
characters that are displayed on the screen (you will not be able to
copy more than one page's worth of characters).

View File

@@ -4,7 +4,7 @@ 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
command or 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
@@ -415,6 +415,11 @@ MouseWheelLeft
MouseWheelRight
```
## Key sequences
Key sequences can be bound by specifying valid keys one after another in brackets, such
as `<Ctrl-x><Ctrl-c>`.
# Default keybinding configuration.
A select few keybindings are different on MacOS compared to other
@@ -530,6 +535,107 @@ conventions for text editing defaults.
}
```
## Pane type bindings
Keybindings can be specified for different pane types as well. For example, to
make a binding that only affects the command bar, use the `command` subgroup:
```
{
"command": {
"Ctrl-w": "WordLeft"
}
}
```
The possible pane types are `buffer` (normal buffer), `command` (command bar),
and `terminal` (terminal pane). The defaults for the command and terminal panes
are given below:
```
{
"terminal": {
"<Ctrl-q><Ctrl-q>": "Exit",
"<Ctrl-e><Ctrl-e>": "CommandMode",
"<Ctrl-w><Ctrl-w>": "NextSplit"
},
"command": {
"Up": "HistoryUp",
"Down": "HistoryDown",
"Right": "CursorRight",
"Left": "CursorLeft",
"ShiftUp": "SelectUp",
"ShiftDown": "SelectDown",
"ShiftLeft": "SelectLeft",
"ShiftRight": "SelectRight",
"AltLeft": "StartOfTextToggle",
"AltRight": "EndOfLine",
"AltUp": "CursorStart",
"AltDown": "CursorEnd",
"AltShiftRight": "SelectWordRight",
"AltShiftLeft": "SelectWordLeft",
"CtrlLeft": "WordLeft",
"CtrlRight": "WordRight",
"CtrlShiftLeft": "SelectToStartOfTextToggle",
"ShiftHome": "SelectToStartOfTextToggle",
"CtrlShiftRight": "SelectToEndOfLine",
"ShiftEnd": "SelectToEndOfLine",
"CtrlUp": "CursorStart",
"CtrlDown": "CursorEnd",
"CtrlShiftUp": "SelectToStart",
"CtrlShiftDown": "SelectToEnd",
"Enter": "ExecuteCommand",
"CtrlH": "Backspace",
"Backspace": "Backspace",
"OldBackspace": "Backspace",
"Alt-CtrlH": "DeleteWordLeft",
"Alt-Backspace": "DeleteWordLeft",
"Tab": "CommandComplete",
"Backtab": "CycleAutocompleteBack",
"Ctrl-z": "Undo",
"Ctrl-y": "Redo",
"Ctrl-c": "CopyLine|Copy",
"Ctrl-x": "Cut",
"Ctrl-k": "CutLine",
"Ctrl-v": "Paste",
"Home": "StartOfTextToggle",
"End": "EndOfLine",
"CtrlHome": "CursorStart",
"CtrlEnd": "CursorEnd",
"Delete": "Delete",
"Ctrl-q": "AbortCommand",
"Ctrl-e": "EndOfLine",
"Ctrl-a": "StartOfLine",
"Ctrl-w": "DeleteWordLeft",
"Insert": "ToggleOverwriteMode",
"Ctrl-b": "WordLeft",
"Ctrl-f": "WordRight",
"Ctrl-d": "DeleteWordLeft",
"Ctrl-m": "ExecuteCommand",
"Ctrl-n": "HistoryDown",
"Ctrl-p": "HistoryUp",
"Ctrl-u": "SelectToStart",
// Emacs-style keybindings
"Alt-f": "WordRight",
"Alt-b": "WordLeft",
"Alt-a": "StartOfText",
"Alt-e": "EndOfLine",
// Integration with file managers
"F10": "AbortCommand",
"Esc": "AbortCommand",
// Mouse bindings
"MouseWheelUp": "HistoryUp",
"MouseWheelDown": "HistoryDown",
"MouseLeft": "MousePress",
"MouseMiddle": "PastePrimary"
}
}
```
## Final notes
Note: On some old terminal emulators and on Windows machines, `Ctrl-h` should be

View File

@@ -54,6 +54,25 @@ Here are the available options:
default value: `false`
* `clipboard`: specifies how micro should access the system clipboard.
Possible values are:
* `external`: accesses clipboard via an external tool, such as xclip/xsel
or wl-clipboard on Linux, pbcopy/pbpaste on MacOS, and system calls on
Windows. On Linux, if you do not have one of the tools installed, or if
they are not working, micro will throw an error and use an internal
clipboard.
* `terminal`: accesses the clipboard via your terminal emulator. Note that
there is limited support among terminal emulators for this feature
(called OSC 52). Terminals that are known to work are Kitty (enable
reading with `clipboard_control` setting), iTerm2 (only copying),
st, rxvt-unicode and xterm if enabled (see `> help copypaste` for
details). Note that Gnome-terminal does not support this feature. With
this setting, copy-paste **will** work over ssh. See `> help copypaste`
for details.
* `internal`: micro will use an internal clipboard.
default value: `external`
* `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.
@@ -375,6 +394,84 @@ Any option you set in the editor will be saved to the file
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.
## Settings.json file
The settings.json file should go in your configuration directory (by default
at `~/.config/micro`), and should contain only options which have been modified
from their default setting. Here is the full list of options in json format,
so that you can see what the formatting should look like.
```json
{
"autoclose": true,
"autoindent": true,
"autosave": 0,
"autosu": false,
"backup": true,
"backupdir": "",
"basename": false,
"clipboard": "external",
"colorcolumn": 0,
"colorscheme": "default",
"comment": true,
"cursorline": true,
"diff": true,
"diffgutter": false,
"divchars": "|-",
"divreverse": true,
"encoding": "utf-8",
"eofnewline": true,
"fastdirty": false,
"fileformat": "unix",
"filetype": "unknown",
"ftoptions": true,
"ignorecase": false,
"indentchar": " ",
"infobar": true,
"initlua": true,
"keepautoindent": false,
"keymenu": false,
"linter": true,
"literate": true,
"matchbrace": true,
"mkparents": false,
"mouse": true,
"parsecursor": false,
"paste": false,
"permbackup": false,
"pluginchannels": [
"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"
],
"pluginrepos": [],
"readonly": false,
"relativeruler": false,
"rmtrailingws": false,
"ruler": true,
"savecursor": false,
"savehistory": true,
"saveundo": false,
"scrollbar": false,
"scrollmargin": 3,
"scrollspeed": 2,
"smartpaste": true,
"softwrap": false,
"splitbottom": true,
"splitright": true,
"status": true,
"statusformatl": "$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
"statusformatr": "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help",
"statusline": true,
"sucmd": "sudo",
"syntax": true,
"tabmovement": false,
"tabsize": 4,
"tabstospaces": false,
"test": true,
"useprimary": true,
"xterm": false
}
```
## Global and local settings
You can set these settings either globally or locally. Locally means that the

View File

@@ -43,6 +43,12 @@ are called when certain events happen. Here is the list of callbacks
which micro defines:
* `init()`: this function should be used for your plugin initialization.
This function is called after buffers have been initialized.
* `preinit()`: initialization function called before buffers have been
initialized.
* `postinit()`: initialization function called after `init()`.
* `onBufferOpen(buf)`: runs when a buffer is opened. The input contains
the buffer object.
@@ -364,8 +370,6 @@ The following functions are also available from the go-humanize package:
The `humanize` package exposes:
* `Bytes`
* `Ordinal`
* `Ftoa`
* `FtoaWithDigits`
## Adding help files, syntax files, or colorschemes in your plugin

View File

@@ -35,6 +35,7 @@ ft["markdown"] = "<!-- %s -->"
ft["nginx"] = "# %s"
ft["nim"] = "# %s"
ft["objc"] = "// %s"
ft["ocaml"] = "(* %s *)"
ft["pascal"] = "{ %s }"
ft["perl"] = "# %s"
ft["php"] = "// %s"
@@ -69,10 +70,38 @@ function onBufferOpen(buf)
end
end
function isCommented(bp, lineN, commentRegex)
local line = bp.Buf:Line(lineN)
if string.match(line, commentRegex) then
return true
end
return false
end
function commentLine(bp, lineN)
local line = bp.Buf:Line(lineN)
local commentType = bp.Buf.Settings["commenttype"]
local commentRegex = "^%s*" .. commentType:gsub("%%","%%%%"):gsub("%$","%$"):gsub("%)","%)"):gsub("%(","%("):gsub("%?","%?"):gsub("%*", "%*"):gsub("%-", "%-"):gsub("%.", "%."):gsub("%+", "%+"):gsub("%]", "%]"):gsub("%[", "%["):gsub("%%%%s", "(.*)")
local sel = -bp.Cursor.CurSelection
local curpos = -bp.Cursor.Loc
local index = string.find(commentType, "%%s") - 1
local commentedLine = commentType:gsub("%%s", trim(line))
bp.Buf:Replace(buffer.Loc(0, lineN), buffer.Loc(#line, lineN), util.GetLeadingWhitespace(line) .. commentedLine)
if bp.Cursor:HasSelection() then
bp.Cursor.CurSelection[1].Y = sel[1].Y
bp.Cursor.CurSelection[2].Y = sel[2].Y
bp.Cursor.CurSelection[1].X = sel[1].X
bp.Cursor.CurSelection[2].X = sel[2].X
else
bp.Cursor.X = curpos.X + index
bp.Cursor.Y = curpos.Y
end
bp.Cursor:Relocate()
bp.Cursor.LastVisualX = bp.Cursor:GetVisualX()
end
function uncommentLine(bp, lineN, commentRegex)
local line = bp.Buf:Line(lineN)
local commentType = bp.Buf.Settings["commenttype"]
local sel = -bp.Cursor.CurSelection
local curpos = -bp.Cursor.Loc
local index = string.find(commentType, "%%s") - 1
@@ -88,46 +117,57 @@ function commentLine(bp, lineN)
bp.Cursor.X = curpos.X - index
bp.Cursor.Y = curpos.Y
end
else
local commentedLine = commentType:gsub("%%s", trim(line))
bp.Buf:Replace(buffer.Loc(0, lineN), buffer.Loc(#line, lineN), util.GetLeadingWhitespace(line) .. commentedLine)
if bp.Cursor:HasSelection() then
bp.Cursor.CurSelection[1].Y = sel[1].Y
bp.Cursor.CurSelection[2].Y = sel[2].Y
bp.Cursor.CurSelection[1].X = sel[1].X
bp.Cursor.CurSelection[2].X = sel[2].X
else
bp.Cursor.X = curpos.X + index
bp.Cursor.Y = curpos.Y
end
end
bp.Cursor:Relocate()
bp.Cursor.LastVisualX = bp.Cursor:GetVisualX()
end
function commentSelection(bp, startLine, endLine)
function toggleCommentLine(bp, lineN, commentRegex)
if isCommented(bp, lineN, commentRegex) then
uncommentLine(bp, lineN, commentRegex)
else
commentLine(bp, lineN)
end
end
function toggleCommentSelection(bp, startLine, endLine, commentRegex)
local allComments = true
for line = startLine, endLine do
commentLine(bp, line)
if not isCommented(bp, line, commentRegex) then
allComments = false
break
end
end
for line = startLine, endLine do
if allComments then
uncommentLine(bp, line, commentRegex)
else
commentLine(bp, line)
end
end
end
function comment(bp, args)
local commentType = bp.Buf.Settings["commenttype"]
local commentRegex = "^%s*" .. commentType:gsub("%%","%%%%"):gsub("%$","%$"):gsub("%)","%)"):gsub("%(","%("):gsub("%?","%?"):gsub("%*", "%*"):gsub("%-", "%-"):gsub("%.", "%."):gsub("%+", "%+"):gsub("%]", "%]"):gsub("%[", "%["):gsub("%%%%s", "(.*)"):gsub("%s+", "%s*")
if bp.Cursor:HasSelection() then
if bp.Cursor.CurSelection[1]:GreaterThan(-bp.Cursor.CurSelection[2]) then
local endLine = bp.Cursor.CurSelection[1].Y
if bp.Cursor.CurSelection[1].X == 0 then
endLine = endLine - 1
end
commentSelection(bp, bp.Cursor.CurSelection[2].Y, endLine)
toggleCommentSelection(bp, bp.Cursor.CurSelection[2].Y, endLine, commentRegex)
else
local endLine = bp.Cursor.CurSelection[2].Y
if bp.Cursor.CurSelection[2].X == 0 then
endLine = endLine - 1
end
commentSelection(bp, bp.Cursor.CurSelection[1].Y, endLine)
toggleCommentSelection(bp, bp.Cursor.CurSelection[1].Y, endLine, commentRegex)
end
else
commentLine(bp, bp.Cursor.Y)
toggleCommentLine(bp, bp.Cursor.Y, commentRegex)
end
end

View File

@@ -9,8 +9,10 @@ following filetypes and linters:
* c++: g++
* d: dmd
* go: go build
* haskell: hlint
* java: javac
* javascript: jshint
* javascript: eslint
* literate: lit
* lua: luacheck
* nim: nim
@@ -19,7 +21,7 @@ following filetypes and linters:
* python: mypy
* python: pylint
* shell: shfmt
* swift: swiftc
* swift: swiftc (MacOS and Linux only)
* yaml: yamllint
If the linter plugin is enabled and the file corresponds to one of
@@ -64,17 +66,17 @@ Below is an example for including a linter for any filetype using
the `misspell` linter which checks for misspelled words in a file.
```lua
local config = import("micro/config")
config.RegisterCommonOption("misspell", true)
function init()
-- uses the default linter plugin
-- matches any filetype
linter.makeLinter("misspell", "", "misspell", {"%f"}, "%f:%l:%c: %m", {}, false, true, 0, 0, hasMisspell)
end
function hasMisspell(buf)
return buf.Settings["misspell"]
linter.makeLinter("misspell", "", "misspell", {"%f"}, "%f:%l:%c: %m", {}, false, true)
end
```
Here is an example for a more typical use-case, where the linter will only match one filetype (C in this case):
```lua
function init()
linter.makeLinter("gcc", "c", "gcc", {"-fsyntax-only", "-Wall", "-Wextra", "%f"}, "%f:%l:%c:.+: %m")
end
```

View File

@@ -59,7 +59,7 @@ function removeLinter(name)
linters[name] = nil
end
function init()
function preinit()
local devnull = "/dev/null"
if runtime.GOOS == "windows" then
devnull = "NUL"
@@ -68,6 +68,7 @@ function init()
makeLinter("gcc", "c", "gcc", {"-fsyntax-only", "-Wall", "-Wextra", "%f"}, "%f:%l:%c:.+: %m")
makeLinter("g++", "c++", "gcc", {"-fsyntax-only","-std=c++14", "-Wall", "-Wextra", "%f"}, "%f:%l:%c:.+: %m")
makeLinter("dmd", "d", "dmd", {"-color=off", "-o-", "-w", "-wi", "-c", "%f"}, "%f%(%l%):.+: %m")
makeLinter("eslint", "javascript", "eslint", {"-f","compact","%f"}, "%f: line %l, col %c, %m")
makeLinter("gobuild", "go", "go", {"build", "-o", devnull, "%d"}, "%f:%l:%c:? %m")
-- makeLinter("golint", "go", "golint", {"%f"}, "%f:%l:%c: %m")
makeLinter("hlint", "haskell", "hlint", {"%f"}, "%f:%l:%c.-: %m")
@@ -80,9 +81,10 @@ function init()
makeLinter("pyflakes", "python", "pyflakes", {"%f"}, "%f:%l:.-:? %m")
makeLinter("mypy", "python", "mypy", {"%f"}, "%f:%l: %m")
makeLinter("pylint", "python", "pylint", {"--output-format=parseable", "--reports=no", "%f"}, "%f:%l: %m")
makeLinter("flake8", "python", "flake8", {"%f"}, "%f:%l:%c: %m")
makeLinter("shfmt", "shell", "shfmt", {"%f"}, "%f:%l:%c: %m")
makeLinter("swiftc", "swift", "xcrun", {"swiftc", "%f"}, "%f:%l:%c:.+: %m", {"darwin"}, true)
makeLinter("swiftc", "swiftc", {"%f"}, "%f:%l:%c:.+: %m", {"linux"}, true)
makeLinter("swiftc", "swift", "swiftc", {"%f"}, "%f:%l:%c:.+: %m", {"linux"}, true)
makeLinter("yaml", "yaml", "yamllint", {"--format", "parsable", "%f"}, "%f:%l:%c:.+ %m")
config.MakeCommand("lint", function(bp, args)
@@ -166,7 +168,6 @@ function onExit(output, args)
elseif col == nil then
hascol = false
end
micro.Log(basename(buf.Path), basename(file))
if basename(buf.Path) == basename(file) then
local bmsg = nil
if hascol then

View File

@@ -12,9 +12,6 @@ rules:
- statement: "\\b(for|if|while|do|else|case|default|switch)\\b"
- statement: "\\b(goto|continue|break|return)\\b"
- preproc: "^[[:space:]]*#[[:space:]]*(define|pragma|include|(un|ifn?)def|endif|el(if|se)|if|warning|error)"
- constant: "'([^'\\\\]|(\\\\[\"'abfnrtv\\\\]))'"
- constant: "'\\\\(([0-3]?[0-7]{1,2}))'"
- constant: "'\\\\x[0-9A-Fa-f]{1,2}'"
# GCC builtins
- statement: "__attribute__[[:space:]]*\\(\\([^)]*\\)\\)"
- statement: "__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__"
@@ -29,15 +26,15 @@ rules:
end: "\""
skip: "\\\\."
rules:
- constant.specialChar: "\\\\."
- constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})"
- constant.string:
start: "'"
end: "'"
skip: "\\\\."
rules:
- preproc: "..+"
- constant.specialChar: "\\\\."
- error: "..+"
- constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})"
- comment:
start: "//"

View File

@@ -4,8 +4,7 @@ detect:
filename: "(\\.c(c|pp|xx)$|\\.h(h|pp|xx)$|\\.ii?$|\\.(def)$)"
rules:
- identifier: "\\b[A-Z_][0-9A-Z_]+\\b"
- identifier: "\\b[A-Z_][0-9A-Z_]+\\b"
- type: "\\b(auto|float|double|bool|char|int|short|long|sizeof|enum|void|static|const|constexpr|struct|union|typedef|extern|(un)?signed|inline)\\b"
- type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr)))_t\\b"
- statement: "\\b(class|namespace|template|public|protected|private|typename|this|friend|virtual|using|mutable|volatile|register|explicit)\\b"
@@ -13,13 +12,12 @@ rules:
- statement: "\\b(try|throw|catch|operator|new|delete)\\b"
- statement: "\\b(goto|continue|break|return)\\b"
- preproc: "^[[:space:]]*#[[:space:]]*(define|pragma|include|(un|ifn?)def|endif|el(if|se)|if|warning|error)"
- constant: "('([^'\\\\]|(\\\\[\"'abfnrtv\\\\]))'|'\\\\(([0-3]?[0-7]{1,2}))'|'\\\\x[0-9A-Fa-f]{1,2}')"
# GCC builtins
- statement: "(__attribute__[[:space:]]*\\(\\([^)]*\\)\\)|__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__)"
# Operator Color
- symbol.operator: "([.:;,+*|=!\\%]|<|>|/|-|&)"
- symbol.operator: "([.:;,+*|=!\\%]|<|>|/|-|&)"
# Parenthetical Color
- symbol.brackets: "[(){}]|\\[|\\]"
@@ -31,15 +29,15 @@ rules:
end: "\""
skip: "\\\\."
rules:
- constant.specialChar: "\\\\."
- constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})"
- constant.string:
start: "'"
end: "'"
skip: "\\\\."
rules:
- preproc: "..+"
- constant.specialChar: "\\\\."
- error: "..+"
- constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})"
- comment:
start: "//"

View File

@@ -5,9 +5,7 @@ detect:
rules:
# Keywords
- statement: "[ ](as|case|of|class|data|default|deriving|do|forall|foreign|hiding|if|then|else|import|infix|infixl|infixr|instance|let|in|mdo|module|newtype|qualified|type|where)[ ]"
- statement: "(^data|^foreign|^import|^infix|^infixl|^infixr|^instance|^module|^newtype|^type)[ ]"
- statement: "[ ](as$|case$|of$|class$|data$|default$|deriving$|do$|forall$|foreign$|hiding$|if$|then$|else$|import$|infix$|infixl$|infixr$|instance$|let$|in$|mdo$|module$|newtype$|qualified$|type$|where$)"
- statement: "\\b(as|case|of|class|data|default|deriving|do|forall|foreign|hiding|if|then|else|import|infix|infixl|infixr|instance|let|in|mdo|module|newtype|qualified|type|where)\\b"
# Various symbols
- symbol: "(\\||@|!|:|_|~|=|\\\\|;|\\(\\)|,|\\[|\\]|\\{|\\})"

View File

@@ -20,7 +20,7 @@ rules:
- statement: "\\b(async|await|break|case|catch|const|continue|debugger|default)\\b"
- statement: "\\b(delete|do|else|export|finally|for|function\\*?|class|extends)\\b"
- statement: "\\b(get|if|import|from|in|of|instanceof|let|new|reject|resolve|return)\\b"
- statement: "\\b(set|super|switch|this|throw|try|typeof|var|void|while|with|yield)\\b"
- statement: "\\b(set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\\b"
# reserved but unassigned
- error: "\\b(enum|implements|interface|package|private|protected|public)"
- constant: "\\b(globalThis|Infinity|null|undefined|NaN)\\b"

View File

@@ -33,11 +33,16 @@ rules:
rules: []
- constant.string:
start: "'''"
end: "'''"
start: "\"[^\"]|\"$"
end: "\""
rules: []
- comment:
start: "#"
start: "#[^=]|#$"
end: "$"
rules: []
- comment:
start: "#="
end: "=#"
rules: []

View File

@@ -12,7 +12,6 @@ rules:
- special: "&[^;[[:space:]]]*;"
- symbol: "[:=]"
- identifier: "(alt|bgcolor|height|href|label|longdesc|name|onclick|onfocus|onload|onmouseover|size|span|src|style|target|type|value|width)="
- constant.string: "\"[^\"]*\""
- constant.number: "(?i)#[0-9A-F]{6,6}"
- constant.string.url: "(ftp(s)?|http(s)?|git|chrome)://[^ ]+"
- comment: "<!--.+?-->"
@@ -33,8 +32,12 @@ rules:
- symbol.operator: "(=>|===|!==|==|!=|&&|\\|\\||::|=|->|\\!)"
- identifier.var: "(\\$[a-zA-Z0-9\\-_]+)"
- symbol.operator: "[\\(|\\)|/|+|\\-|\\*|\\[|.|,|;]"
- constant.string: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'"
- constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]"
- constant.string:
start: "\""
end: "\""
skip: "\\\\."
rules:
- constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]"
- symbol.brackets: "(\\[|\\]|\\{|\\}|[()])"
- comment: "(^|[[:space:]])//.*"
- comment: "(^|[[:space:]])#.*"

View File

@@ -6,13 +6,13 @@ detect:
rules:
# built-in objects
- constant: "\\b(Ellipsis|None|self|True|False)\\b"
- constant: "\\b(Ellipsis|None|self|cls|True|False)\\b"
# built-in attributes
- constant: "\\b(__bases__|__builtin__|__class__|__debug__|__dict__|__doc__|__file__|__members__|__methods__|__name__|__self__)\\b"
# built-in functions
- identifier: "\\b(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes|callable|chr|classmethod|compile|complex|delattr|dir|divmod|eval|exec|format|getattr|globals|hasattr|hash|help|hex|id|input|isinstance|issubclass|iter|len|locals|max|min|next|nonlocal|oct|open|ord|pow|print|repr|round|setattr|sorted|sum|vars|__import__)\\b"
# special method names
- identifier: "\\b(__abs__|__add__|__and__|__call__|__cmp__|__coerce__|__complex__|__concat__|__contains__|__del__|__delattr__|__delitem__|__delslice__|__div__|__divmod__|__float__|__getattr__|__getitem__|__getslice__|__hash__|__hex__|__init__|__int__|__inv__|__invert__|__len__|__dict__|__long__|__lshift__|__mod__|__mul__|__neg__|__next__|__nonzero__|__oct__|__or__|__pos__|__pow__|__radd__|__rand__|__rcmp__|__rdiv__|__rdivmod__|__repeat__|__repr__|__rlshift__|__rmod__|__rmul__|__ror__|__rpow__|__rrshift__|__rshift__|__rsub__|__rxor__|__setattr__|__setitem__|__setslice__|__str__|__sub__|__xor__)\\b"
- identifier: "\\b__(abs|add|and|call|cmp|coerce|complex|concat|contains|delattr|delitem|delslice|del|dict|divmod|div|float|getattr|getitem|getslice|hash|hex|iadd|iand|iconcat|ifloordiv|ilshift|imatmul|imod|imul|init|int|invert|inv|ior|ipow|irshift|isub|iter|itruediv|ixor|len|long|lshift|mod|mul|neg|next|nonzero|oct|or|pos|pow|radd|rand|rcmp|rdivmod|rdiv|repeat|repr|rlshift|rmod|rmul|ror|rpow|rrshift|rshift|rsub|rxor|setattr|setitem|setslice|str|sub|xor)__\\b"
# types
- type: "\\b(bool|bytearray|bytes|classmethod|complex|dict|enumerate|filter|float|frozenset|int|list|map|memoryview|object|property|range|reversed|set|slice|staticmethod|str|super|tuple|type|zip)\\b"
# definitions
@@ -22,11 +22,14 @@ rules:
# decorators
- brightgreen: "@.*[(]"
# operators
- symbol.operator: "([.:;,+*|=!\\%@]|<|>|/|-|&)"
- symbol.operator: "([~^.:;,+*|=!\\%@]|<|>|/|-|&)"
# parentheses
- symbol.brackets: "([(){}]|\\[|\\])"
# numbers
- constant.number: "\\b[0-9]+\\b"
- constant.number: "\\b[1-9](_?[0-9])*(\\.([0-9](_?[0-9])*)?)?(e[0-9](_?[0-9])*)?\\b" # decimal
- constant.number: "\\b0b(_?[01])+\\b" # bin
- constant.number: "\\b0o(_?[0-7])+\\b" # oct
- constant.number: "\\b0x(_?[0-9a-f])+\\b" # hex
- constant.string:
start: "\"\"\""
@@ -40,14 +43,14 @@ rules:
- constant.string:
start: "\""
end: "\""
end: "(\"|$)"
skip: "\\\\."
rules:
- constant.specialChar: "\\\\."
- constant.string:
start: "'"
end: "'"
end: "('|$)"
skip: "\\\\."
rules:
- constant.specialChar: "\\\\."
@@ -55,5 +58,5 @@ rules:
- comment:
start: "#"
end: "$"
rules: []
rules:
- todo: "(TODO|FIXME):?"

15
runtime/syntax/renpy.yaml Normal file
View File

@@ -0,0 +1,15 @@
filetype: renpy
detect:
filename: "\\.rpy$"
rules:
# Script language keywords.
- statement: "\\b(python|init|early|define|default|label|call|jump|image|layeredimage|screen|style|transform|menu|show|hide|scene|at|with|zorder|behind|pause|play|stop|fadeout|fadein|queue)\\b"
# ATL keywords.
- type: "\\b(repeat|block|choice|parallel|(x|y|)(pos|offset|anchor|align|center|tile|zoom)|time|linear|easein|alpha|subpixel)\\b"
- identifier: "\\bpersistent\\b"
- special: "\\$ "
# Tab characters are not allowed in Renpy scripts.
- error: "\\t"
- include: python

View File

@@ -7,15 +7,19 @@ rules:
# function definition
- identifier: "fn [a-z0-9_]+"
# Reserved words
- statement: "\\b(abstract|alignof|as|become|box|break|const|continue|crate|do|else|enum|extern|false|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|offsetof|override|priv|pub|pure|ref|return|sizeof|static|self|struct|super|true|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\\b"
- statement: "\\b(abstract|alignof|as|become|box|break|const|continue|crate|do|dyn|else|enum|extern|false|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|offsetof|override|priv|pub|pure|ref|return|sizeof|static|self|struct|super|true|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\\b"
# macros
- special: "[a-z_]+!"
# Constants
- constant: "[A-Z][A-Z_]+"
- constant: "\\b[A-Z][A-Z_0-9]+\\b"
# Numbers
- constant.number: "\\b[0-9]+\\b"
# Booleans
- constant: "\\b(true|false)\\b"
# Traits/Enums/Structs/Types/etc.
- type: "[A-Z][a-z]+"
- type: "\\b[A-Z]+[a-zA-Z_0-9]*[a-z]+[a-zA-Z_0-9]*\\b"
# Builtin types that start with lowercase.
- type: "\\b(bool|str|isize|usize|((i|u)(8|16|32|64))|f32|f64)\\b"
- constant.string:
start: "\""

View File

@@ -4,34 +4,50 @@ detect:
filename: "\\.zig$"
rules:
# function definition
- identifier: "fn [a-z0-9_]+"
# Reserved words
- statement: "\\b(align|and|allowzero|anyerror|asm|async|await|break|cancel|catch|comptime|const|continue|defer|else|enum|errdefer|error|export|extern|false|fn|for|if|inline|nakedcc|noalias|null|or|orelse|packed|promise|pub|resume|return|linksection|stdcallcc|struct|suspend|switch|test|threadlocal|true|try|undefined|union|unreachable|use|var|volatile|while)\\b"
- statement: "\\b(align|allowzero|and|asm|async|await|break|callconv|catch|comptime|const|continue|defer|else|errdefer|error|export|extern|fn|for|if|inline|noalias|noinline|nosuspend|or|orelse|packed|pub|resume|return|linksection|suspend|switch|test|threadlocal|try|unreachable|usingnamespace|var|volatile|while)\\b"
# builtin functions
- special: "@+[a-z_]+"
# Constants
- constant: "[A-Z][A-Z_]+([0-9]+)?"
# Numbers (hexadecimal + decimal)
- constant.number: "\\b(0x[A-F0-9]+|[0-9]+)\\b"
# Primitive Types / Derived Data Types
- type: "\\b([A-Z][a-z]+|(i8|u8|i16|u16|i32|u32|i64|u64|i128|u128|isize|usize|c_short|c_ushort|c_int|c_uint|c_long|c_ulong|c_longlong|c_ulonglong|c_longdouble|c_void|f16|f32|f64|f128|bool|void|noreturn|type|anyerror|comptime_int|comptime_float))\\b"
- special: "@[a-zA-Z_]+"
# Primitive Types
- type: "\\b(anyframe|anytype|anyerror|bool|comptime_int|comptime_float|enum|f(16|32|64|128)|isize|noreturn|struct|type|union|usize|void)\\b"
- type: "\\b(c_u?(short|int|long(long)?)|c_longdouble|c_void)\\b"
- type: "\\b((i|u)[0-9]+)\\b"
# Operators
- symbol.operator: "[-!|=;%.+^*:&?<>~]"
# Parenthesis
- symbol.brackets: "[(){}]|\\[|\\]"
# Constants
- constant: "\\b(null|undefined)\\b"
- constant.number: "\\b(0b[01_]+|0o[0-7_]+|[0-9_]+|0x[a-fA-F0-9_]+)\\b"
- constant.bool: "\\b(true|false)\\b"
- constant.string:
start: "\""
end: "\""
skip: "\\\\."
rules:
- constant.specialChar: "\\\\."
- constant.specialChar: "\\\\([nrt\\\\'\"]|x[a-fA-F0-9]{2}|u{[a-fA-F0-9]+})"
- constant.string:
start: "'"
end: "'"
skip: "\\\\."
rules:
- error: "..+"
- constant.specialChar: "\\\\([nrt\\\\'\"]|x[a-fA-F0-9]{2}|u{[a-fA-F0-9]+})"
- constant.string:
start: "\\\\\\\\"
end: "$"
skip: "\\\\."
rules:
- constant.specialChar: "\\\\([nrt\\\\'\"]|x[a-fA-F0-9]{2}|u{[a-fA-F0-9]+})"
- comment:
start: "//"
end: "$"
rules:
- todo: "(TODO|XXX|FIXME):?"
- comment:
start: "/\\*"
end: "\\*/"
rules:
- todo: "(TODO|XXX|FIXME):?"

View File

@@ -2,9 +2,23 @@ package main
import (
"fmt"
"os"
"strconv"
"time"
)
func main() {
fmt.Println(time.Now().Local().Format("January 02, 2006"))
var buildTime time.Time
epoch := os.Getenv("SOURCE_DATE_EPOCH")
if epoch != "" {
i, err := strconv.Atoi(epoch)
if err != nil {
fmt.Errorf("SOURCE_DATE_EPOCH is not a valid integer")
os.Exit(1)
}
buildTime = time.Unix(int64(i), 0)
} else {
buildTime = time.Now().Local()
}
fmt.Println(buildTime.Format("January 02, 2006"))
}

View File

@@ -6,6 +6,9 @@ mkdir -p micro-$1
cp LICENSE micro-$1
cp README.md micro-$1
cp LICENSE-THIRD-PARTY micro-$1
cp assets/packaging/micro.1 micro-$1
cp assets/packaging/micro.desktop micro-$1
cp assets/micro-logo-mark.svg micro-$1/micro.svg
HASH="$(git rev-parse --short HEAD)"
VERSION="$(go run tools/build-version.go)"
@@ -22,6 +25,9 @@ mv micro-$1-osx.tar.gz binaries
# Linux
echo "Linux 64"
GOOS=linux GOARCH=amd64 make build
./tools/package-deb.sh $1
mv micro-$1-amd64.deb binaries
mv micro micro-$1
tar -czf micro-$1-linux64.tar.gz micro-$1
mv micro-$1-linux64.tar.gz binaries

View File

@@ -1,35 +1,25 @@
# This script creates the nightly release on Github for micro
# This script updates the nightly release on Github for micro
# Must be run from inside the micro git repository
commitID=$(git rev-parse --short HEAD)
# info=$(github-release info -u zyedidia -r micro -t nightly)
# if [[ $info = *$commitID* ]]; then
# echo "No new commits since last nightly"
# exit 1
# fi
go run remove-nightly-assets.go
# echo "Moving tag"
# hub push origin :refs/tags/nightly
# git tag -f nightly $commitID
# hub push --tags
echo "Cross compiling binaries"
./cross-compile.sh $1
mv ../binaries .
MESSAGE=$'Nightly build\n\nAutogenerated nightly build of micro'
echo "Creating new release"
echo "Updating release"
hub release edit nightly \
--prerelease \
--draft=false \
--message "$MESSAGE. Assets uploaded on $(date) for commit $commitID." \
--message "$MESSAGE (please DISREGARD the creation date of this Github release). Assets uploaded on $(date) for commit $commitID." \
--attach "binaries/micro-$1-osx.tar.gz" \
--attach "binaries/micro-$1-linux64.tar.gz" \
--attach "binaries/micro-$1-linux64-static.tar.gz" \
--attach "binaries/micro-$1-amd64.deb" \
--attach "binaries/micro-$1-linux32.tar.gz" \
--attach "binaries/micro-$1-linux-arm.tar.gz" \
--attach "binaries/micro-$1-linux-arm64.tar.gz" \

1
tools/package-deb.sh Executable file
View File

@@ -0,0 +1 @@
fpm -s dir -t deb -p micro-$1-amd64.deb --name micro --license mit --version $1 --deb-recommends xclip --description "A modern and intuitive terminal-based text editor" ./micro=/usr/bin/micro ./assets/packaging/micro.1=/usr/share/man/man1/micro.1

View File

@@ -23,6 +23,7 @@ hub release create $tag \
--attach "binaries/micro-$1-osx.tar.gz" \
--attach "binaries/micro-$1-linux64.tar.gz" \
--attach "binaries/micro-$1-linux64-static.tar.gz" \
--attach "binaries/micro-$1-amd64.deb" \
--attach "binaries/micro-$1-linux32.tar.gz" \
--attach "binaries/micro-$1-linux-arm.tar.gz" \
--attach "binaries/micro-$1-linux-arm64.tar.gz" \

View File

@@ -22,6 +22,7 @@ hub release create $tag \
--attach "binaries/micro-$1-osx.tar.gz" \
--attach "binaries/micro-$1-linux64.tar.gz" \
--attach "binaries/micro-$1-linux64-static.tar.gz" \
--attach "binaries/micro-$1-amd64.deb" \
--attach "binaries/micro-$1-linux32.tar.gz" \
--attach "binaries/micro-$1-linux-arm.tar.gz" \
--attach "binaries/micro-$1-linux-arm64.tar.gz" \

13
tools/update-nightly-tag.sh Executable file
View File

@@ -0,0 +1,13 @@
commitID=$(git rev-parse --short HEAD)
echo "Moving tag"
hub push origin :refs/tags/nightly
git tag -f nightly $commitID
hub push --tags
MESSAGE=$'Nightly build\n\nAutogenerated nightly build of micro'
echo "Creating new release"
hub release create nightly \
--prerelease \
--draft=false \
--message "$MESSAGE."