Compare commits

...

86 Commits

Author SHA1 Message Date
Leo Arias
fad95c028a install the snap with make (#747)
* install the snap with make

* set the source-type to git, remove get

* remove extra space
2017-07-26 18:35:16 -04:00
Zachary Yedidia
5c462f5600 Always use custom syntax files over default
The custom syntax files were first in the array but micro should stop
loading files into the filetype if it already found a match.

Fixes #750
2017-07-23 20:22:47 -04:00
Zachary Yedidia
5dcc486214 Initialize submodules during makefile build 2017-07-21 17:53:26 -04:00
Zachary Yedidia
9d2915c328 Fix local import for tools/semver 2017-07-21 08:35:51 -04:00
Tatsuro YOKOTA
4f8f6f1ca3 multi-byte characters are overlapped in prompt message (#745)
* modified messenger.Display

* modified messenger.Display
2017-07-20 22:18:15 -04:00
Zachary Yedidia
fbf58486fb Merge pull request #743 from mfxmfx/rebuild-runtime
Rebuild runtime.go.
2017-07-18 12:55:27 -04:00
Markus F.X.J. Oberhumer
97aae225da Rebuild runtime.go. 2017-07-17 07:21:38 +02:00
Zachary Yedidia
1ca2debd7c Merge pull request #741 from adrian5/patch-1
Add consistent spacing to help file
2017-07-16 10:19:43 -04:00
adrian5
c1584dd72f Add consistent spacing to help file 2017-07-16 02:00:30 +02:00
Zachary Yedidia
2bbd29998e Merge 2017-07-15 17:53:40 -04:00
Zachary Yedidia
3b2d7abe3d Store visual x when cursor loc moves in certain cases
Fixes #739
2017-07-15 17:52:56 -04:00
Zachary Yedidia
994d1acbfc Merge pull request #720 from bvaudour/master
Add twig syntax (https://twig.sensiolabs.org/)
2017-07-12 16:10:48 -04:00
Zachary Yedidia
1f4ae1e2d5 Add semver code to tools directory
Ref #736
2017-07-12 09:36:16 -04:00
Zachary Yedidia
18ad74a982 Merge pull request #734 from eyelash/vala-types
improved highlighting for Vala types
2017-07-11 10:46:39 -04:00
Elias Aebi
4cad06c7b3 improved highlighting for Vala types 2017-07-11 14:40:38 +02:00
Zachary Yedidia
ec77dccb1d Merge 2017-07-10 18:04:40 -04:00
Zachary Yedidia
63b4848bb0 Make sure screen is not nil before pollevent
Ref #728
Ref #727
2017-07-10 18:04:11 -04:00
Zachary Yedidia
e27802c41e Merge pull request #726 from DanielPower/patch-1
Remove `new` keyword from C syntax
2017-07-10 08:49:38 -04:00
DanielPower
75329830f9 Remove new keyword from C syntax
Fixes #725
2017-07-10 00:36:49 -02:30
Zachary Yedidia
030a05c103 Always restart the screen when saving with sudo
Fixes #723
2017-07-08 15:13:42 -04:00
Zachary Yedidia
e4751fd84c Optimize search
Fixes #722
2017-07-08 15:03:35 -04:00
Zachary Yedidia
252def5b95 Fix recursive search and replace in special case
Fixes #717
2017-07-01 16:50:25 -04:00
Zachary Yedidia
42f2af7956 Print help text to stdout instead of stderr
Fixes #719
2017-07-01 16:49:08 -04:00
Zachary Yedidia
91fb8225d1 Add 'a' option in replace prompt
Closes #718
2017-07-01 16:47:50 -04:00
Zachary Yedidia
bee60023ae Optimize search and replace 2017-07-01 16:40:28 -04:00
bvaudour
0ffae1896b Add twig syntax (https://twig.sensiolabs.org/) 2017-06-30 12:31:18 +02:00
Zachary Yedidia
8f4820ba28 Move cursor selections on Insert or Remove
Ref #715
2017-06-25 19:14:01 -04:00
Zachary Yedidia
3a02ad8664 Fix vendor-src script 2017-06-23 17:28:33 -04:00
Zachary Yedidia
244e0ded60 Fix ordering of tagging and compiling in release script 2017-06-23 17:26:06 -04:00
Zachary Yedidia
19926d95fe Throw error if there is no detect regex
Closes #712
2017-06-22 10:42:06 -04:00
Zachary Yedidia
f27ee60149 Fix compilation errors 2017-06-21 14:37:30 -04:00
Zachary Yedidia
3908813afe Recover errors in highlight
Ref #712
2017-06-21 14:26:35 -04:00
Zachary Yedidia
e6f24b0924 Add new tcell commits 2017-06-20 17:47:39 -04:00
Zachary Yedidia
b2c1c8f8db Merge pull request #710 from HeavyHorst/master
fixed a panic when a line is cut out(strg+k) and the next line is empty.
2017-06-20 16:52:48 -04:00
Zachary Yedidia
5a2f9a374b Merge pull request #709 from mfxmfx/xterm-ctrl-pgup
Add support for xterm CtrlPgUp and CtrlPgDn keys.
2017-06-20 10:53:31 -04:00
Zachary Yedidia
59ab5107bf Add new commits to tcell (ctrlpageup/down) 2017-06-20 10:53:07 -04:00
Rene Kaufmann
06c65d8404 fixed a panic when a line is cut out(strg+k) and the next line
is empty.
2017-06-20 11:53:59 +02:00
Markus F.X.J. Oberhumer
d38055f825 Add support for xterm CtrlPgUp and CtrlPgDn keys. 2017-06-19 19:37:55 +02:00
Zachary Yedidia
4aeb4c78ac Merge 2017-06-18 15:39:50 -04:00
Zachary Yedidia
3741a71cc5 Check bounds on LastVisualX
Fixes #708
2017-06-18 15:38:33 -04:00
Zachary Yedidia
fc9ddaf941 Merge pull request #679 from alialaee/master
Find and replace one at a time
2017-06-18 12:13:43 -04:00
Zachary Yedidia
1f6a9cfa46 Merge pull request #700 from Calinou/darcula-tc-colorscheme
Add a Darcula colorscheme
2017-06-18 12:12:21 -04:00
Hugo Locurcio
1a18fad1a4 Add a Darcula colorscheme
It is ideally used in truecolor mode, but it also approximates well to
a 256-color palette.
2017-06-18 17:59:03 +02:00
Zachary Yedidia
39b5c4746e Don't vendor src in cross compile script 2017-06-18 10:38:57 -04:00
Zachary Yedidia
5ec08d0a29 Use correct separator on windows
Ref #673
2017-06-18 09:56:36 -04:00
Zachary Yedidia
7ec222895c Update makefile 2017-06-18 09:54:07 -04:00
Zachary Yedidia
da1ec3132f Update tcell 2017-06-17 23:02:22 -04:00
Zachary Yedidia
167f1e5770 Use the idle-wakeup-fix-forked branch of tcell 2017-06-17 22:54:05 -04:00
Zachary Yedidia
8719b9d75c Update readme 2017-06-17 18:00:26 -04:00
Zachary Yedidia
af1f161b06 Update makefile
Since dependencies are now vendored, there is no need to update them in
the makefile.
2017-06-17 17:54:08 -04:00
Zachary Yedidia
7e4ff05c57 Use submodules for dependency management 2017-06-17 17:52:28 -04:00
Zachary Yedidia
f1ecd37578 Update readme 2017-06-17 17:42:43 -04:00
Zachary Yedidia
7ccec0e3f7 Merge pull request #704 from zyedidia/multiple-cursors
Multiple cursors
2017-06-17 17:39:31 -04:00
Zachary Yedidia
397361f23d Add multiple cursor docs + improve docs in general 2017-06-17 17:36:27 -04:00
Zachary Yedidia
681da2e90c Deselect with mouse
This commit also makes non editing actions (save, quit...) only execute
once even if there are multiple cursors.
2017-06-17 11:05:23 -04:00
Zachary Yedidia
118e6b1804 Merge cursors properly
Cursors will merge together if they are on top of each other.
2017-06-17 10:43:14 -04:00
Zachary Yedidia
f933b90c66 Get undo working properly with multiple cursors 2017-06-16 22:19:33 -04:00
Zachary Yedidia
21840d3ffe Make cursor movement automatic on insert + remove
This changes the behavior of cursor movement so that all cursors are
adjusted when a change is made to the buffer. Cursors don't have to be
manually moved after calling Insert or Remove, those functions will move
the cursor properly on their own.

This should fix issues 1-3 mentioned in the multiple cursors discussion.

Ref #5
2017-06-15 18:52:51 -04:00
Zachary Yedidia
5e80ab9362 Merge pull request #697 from frankbraun/spaces
Implement Spaces() with with strings.Repeat()
2017-06-14 15:58:32 -04:00
Frank Braun
43eb238b08 Implement Spaces() with with strings.Repeat()
Shorter and more efficient.
2017-06-14 19:47:40 +00:00
Zachary Yedidia
00718f99cf Add ability to add cursors with Ctrl-MouseLeft
With the new code that allows binding mouse buttons this was remarkably
easy to add.

The new binding is:

    "Ctrl-MouseLeft": "MouseMultiCursor"

Note: A number of terminals don't support Ctrl-MouseLeft (macOS
especially) so you might want to rebind to MouseRight or MouseMiddle.
2017-06-12 20:25:10 -04:00
Zachary Yedidia
c3a73d63b8 Add comments 2017-06-12 20:25:10 -04:00
Zachary Yedidia
bc3c8eaf74 Use terminal cursor for the base cursor
If all cursors fake then that breaks support for things like
inserting japanese characters nicely, so fake cursors are now only used
as extra cursors.
2017-06-12 20:25:10 -04:00
Zachary Yedidia
8d268ef021 Remove debug messages 2017-06-12 20:25:10 -04:00
Zachary Yedidia
c45ff4dd4f Add multiple cursor support
This commit creates new keybindings and actions to handle multiple
cursors.

Here are the defaults:

    "Alt-n": "SpawnMultiCursor",
    "Alt-p": "RemoveMultiCursor",
    "Alt-c": "RemoveAllMultiCursors",
    "Alt-x": "SkipMultiCursor",
2017-06-12 20:25:10 -04:00
Zachary Yedidia
37ad137012 Properly show end of line selection 2017-06-12 15:49:21 -04:00
Zachary Yedidia
0165c4a40a Allow binding runes to actions
This new functionality would make it possible to emulate vim keybindings
pretty easily, for example.
2017-06-11 20:40:11 -04:00
Zachary Yedidia
3270acdd00 Add functionality for binding mouse buttons
This commit enables users to bind the mouse buttons (left, middle,
right buttons and the scroll wheel).

The default bindings now include the mouse bindings:

    "MouseWheelUp":   "ScrollUp",
    "MouseWheelDown": "ScrollDown",
    "MouseLeft":      "MousePress",
    "MouseMiddle":    "PastePrimary",

Mouse buttons can now also be bound to normal actions. For example:

    "MouseLeft": "Backspace"

This also means that plugins can access mouse event callbacks in the
standard way ('onAction').

More documentation for this will be coming soon.

Fixes #542
2017-06-11 17:49:59 -04:00
Zachary Yedidia
ee84296dfe Merge pull request #695 from elopio/snapcraft-update
update the snapcraft.yaml
2017-06-11 12:25:17 -04:00
Leo Arias
42849e7104 update the snapcraft.yaml 2017-06-11 16:11:07 +00:00
Zachary Yedidia
a1f6dd6f4f Update default colorscheme 2017-06-10 18:52:08 -04:00
Tommy
47cdfb3de0 added crontab highlighting. (#689)
* added crontab highlighting.

* added day and month keywords. added label keywords.
2017-06-10 15:24:46 -04:00
Zachary Yedidia
ac362bf1db Merge pull request #694 from tommyshem/xresources
Correct the comment from # to ! as pointed out in the gitter chat on Xresources files high lighting
2017-06-10 11:20:55 -04:00
tommy
462f73f695 Correct the comment from # to ! as point out in the gitter chat. 2017-06-10 11:33:50 +01:00
Zachary Yedidia
cf92f91e1e Improve solarized-tc colorscheme 2017-06-07 12:41:20 -04:00
Yurizal Susanto
52d6ac6cda Appstream Metainfo change (#692)
* Remove strange characters in some line

* Move the xml to data folder
2017-06-05 14:03:39 -04:00
Zachary Yedidia
eeb2aaf9ae Merge 2017-06-04 19:17:08 -04:00
Zachary Yedidia
f84c9f3b5d More descriptive error for 'terminal entry not found' 2017-06-04 19:17:02 -04:00
Zachary Yedidia
be56918174 Merge pull request #683 from yursan9/appstream
Add AppStream metainfo
2017-06-02 20:24:03 -04:00
Zachary Yedidia
08daaf95e4 Merge pull request #688 from tommyshem/help-defaultkeys
Added missing defualt key bindings to help_defaultkeys.md
2017-06-02 20:23:33 -04:00
tommy
51d73c6618 Added missing defualt key bindings and changed micro section to same format. Moved tab key bindings to its own heading. 2017-06-01 06:35:08 +01:00
Tommy
4644a2b5cc Check for readonly on viewtype and if true do not let any edits or paste to buffer. eg help and plugins. Finished needs checking. (#674)
* corrected spelling error and missing public function comments

* check for readonly and if true do not insert character

* mouse middle  click checks for view type readonly and does not paste if view is readonly

* check for view readonly with binding keys and if readonly do not change the content.
2017-05-29 14:18:10 -04:00
Zachary Yedidia
89863660ba Fix boolean logic order for hiding cursor
Fixes #684
2017-05-28 19:52:56 -04:00
Zachary Yedidia
641d188997 Update readme 2017-05-28 10:52:54 -04:00
Yurizal Susanto
226932e631 Add AppStream metainfo 2017-05-28 12:11:09 +07:00
ali
480a220fda Change replace command default behaviour to confirm replacement for each occurrences 2017-05-26 03:43:57 +04:30
65 changed files with 2826 additions and 1718 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
.DS_Store
micro
!cmd/micro
binaries/

57
.gitmodules vendored Normal file
View File

@@ -0,0 +1,57 @@
[submodule "cmd/micro/vendor/github.com/blang/semver"]
path = cmd/micro/vendor/github.com/blang/semver
url = https://github.com/blang/semver
[submodule "cmd/micro/vendor/github.com/dustin/go-humanize"]
path = cmd/micro/vendor/github.com/dustin/go-humanize
url = https://github.com/dustin/go-humanize
[submodule "cmd/micro/vendor/github.com/go-errors/errors"]
path = cmd/micro/vendor/github.com/go-errors/errors
url = https://github.com/go-errors/errors
[submodule "cmd/micro/vendor/github.com/mattn/go-isatty"]
path = cmd/micro/vendor/github.com/mattn/go-isatty
url = https://github.com/mattn/go-isatty
[submodule "cmd/micro/vendor/github.com/mattn/go-runewidth"]
path = cmd/micro/vendor/github.com/mattn/go-runewidth
url = https://github.com/mattn/go-runewidth
[submodule "cmd/micro/vendor/github.com/mitchellh/go-homedir"]
path = cmd/micro/vendor/github.com/mitchellh/go-homedir
url = https://github.com/mitchellh/go-homedir
[submodule "cmd/micro/vendor/github.com/sergi/go-diff"]
path = cmd/micro/vendor/github.com/sergi/go-diff
url = https://github.com/sergi/go-diff
[submodule "cmd/micro/vendor/github.com/yuin/gopher-lua"]
path = cmd/micro/vendor/github.com/yuin/gopher-lua
url = https://github.com/yuin/gopher-lua
[submodule "cmd/micro/vendor/golang.org/x/net"]
path = cmd/micro/vendor/golang.org/x/net
url = https://go.googlesource.com/net
[submodule "cmd/micro/vendor/github.com/zyedidia/clipboard"]
path = cmd/micro/vendor/github.com/zyedidia/clipboard
url = https://github.com/zyedidia/clipboard
[submodule "cmd/micro/vendor/github.com/zyedidia/glob"]
path = cmd/micro/vendor/github.com/zyedidia/glob
url = https://github.com/zyedidia/glob
[submodule "cmd/micro/vendor/github.com/zyedidia/json5"]
path = cmd/micro/vendor/github.com/zyedidia/json5
url = https://github.com/zyedidia/json5
[submodule "cmd/micro/vendor/github.com/zyedidia/tcell"]
path = cmd/micro/vendor/github.com/zyedidia/tcell
url = https://github.com/zyedidia/tcell
[submodule "cmd/micro/vendor/github.com/gdamore/encoding"]
path = cmd/micro/vendor/github.com/gdamore/encoding
url = https://github.com/gdamore/encoding
[submodule "cmd/micro/vendor/golang.org/x/text"]
path = cmd/micro/vendor/golang.org/x/text
url = https://go.googlesource.com/text
[submodule "cmd/micro/vendor/github.com/lucasb-eyer/go-colorful"]
path = cmd/micro/vendor/github.com/lucasb-eyer/go-colorful
url = https://github.com/lucasb-eyer/go-colorful
[submodule "cmd/micro/vendor/layeh.com/gopher-luar"]
path = cmd/micro/vendor/layeh.com/gopher-luar
url = https://github.com/layeh/gopher-luar
[submodule "cmd/micro/vendor/gopkg.in/yaml.v2"]
path = cmd/micro/vendor/gopkg.in/yaml.v2
url = https://gopkg.in/yaml.v2
[submodule "cmd/micro/vendor/github.com/zyedidia/poller"]
path = cmd/micro/vendor/github.com/zyedidia/poller
url = https://github.com/zyedidia/poller

View File

@@ -11,7 +11,7 @@ ADDITIONAL_GO_LINKER_FLAGS := $(shell GOOS=$(shell go env GOHOSTOS) \
GOBIN ?= $(shell go env GOPATH)/bin
# Builds micro after checking dependencies but without updating the runtime
build: deps
build: update
go build -ldflags "-s -w -X main.Version=$(VERSION) -X main.CommitHash=$(HASH) -X 'main.CompileDate=$(DATE)' $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
# Builds micro after building the runtime and checking dependencies
@@ -22,7 +22,7 @@ build-quick:
go build -ldflags "-s -w -X main.Version=$(VERSION) -X main.CommitHash=$(HASH) -X 'main.CompileDate=$(DATE)' $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
# Same as 'build' but installs to $GOBIN afterward
install: deps
install: update
go install -ldflags "-s -w -X main.Version=$(VERSION) -X main.CommitHash=$(HASH) -X 'main.CompileDate=$(DATE)' $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
# Same as 'build-all' but installs to $GOBIN afterward
@@ -32,13 +32,9 @@ install-all: runtime install
install-quick:
go install -ldflags "-s -w -X main.Version=$(VERSION) -X main.CommitHash=$(HASH) -X 'main.CompileDate=$(DATE)' $(ADDITIONAL_GO_LINKER_FLAGS)" ./cmd/micro
# Checks for dependencies
deps:
go get -d ./cmd/micro
update:
git pull
go get -u -d ./cmd/micro
git submodule update --init
# Builds the runtime
runtime:
@@ -47,7 +43,6 @@ runtime:
mv runtime.go cmd/micro
test:
go get -d ./cmd/micro
go test ./cmd/micro
clean:

View File

@@ -23,6 +23,7 @@ You can also check out the website for Micro at https://micro-editor.github.io.
* Easy to use and to install
* No dependencies or external files are needed -- just the binary you can download further down the page
* Multiple cursors
* Common keybindings (ctrl-s, ctrl-c, ctrl-v, ctrl-z...)
* Keybindings can be rebound to your liking
* Sane defaults
@@ -94,13 +95,15 @@ If your operating system does not have a binary release, but does run Go, you ca
Make sure that you have Go version 1.5 or greater (Go 1.4 will work if your version supports CGO) and that your `GOPATH` env variable is set (I recommand setting it to `~/go` if you don't have one).
```
go get -d github.com/zyedidia/micro/...
go get -d github.com/zyedidia/micro/cmd/micro
cd $GOPATH/src/github.com/zyedidia/micro
make install
```
The binary will then be installed to `$GOPATH/bin` (or your `$GOBIN`).
Please make sure that when you are working with micro's code, you are working on your `GOPATH`.
You can install directly with `go get` (`go get -u github.com/zyedidia/micro/cmd/micro`) but this isn't recommended because it doesn't build micro with version information which is useful for the plugin manager.
### MacOS terminal

File diff suppressed because it is too large Load Diff

View File

@@ -10,92 +10,114 @@ import (
)
var bindings map[Key][]func(*View, bool) bool
var mouseBindings map[Key][]func(*View, bool, *tcell.EventMouse) bool
var helpBinding string
var mouseBindingActions = map[string]func(*View, bool, *tcell.EventMouse) bool{
"MousePress": (*View).MousePress,
"MouseMultiCursor": (*View).MouseMultiCursor,
}
var bindingActions = map[string]func(*View, bool) bool{
"CursorUp": (*View).CursorUp,
"CursorDown": (*View).CursorDown,
"CursorPageUp": (*View).CursorPageUp,
"CursorPageDown": (*View).CursorPageDown,
"CursorLeft": (*View).CursorLeft,
"CursorRight": (*View).CursorRight,
"CursorStart": (*View).CursorStart,
"CursorEnd": (*View).CursorEnd,
"SelectToStart": (*View).SelectToStart,
"SelectToEnd": (*View).SelectToEnd,
"SelectUp": (*View).SelectUp,
"SelectDown": (*View).SelectDown,
"SelectLeft": (*View).SelectLeft,
"SelectRight": (*View).SelectRight,
"WordRight": (*View).WordRight,
"WordLeft": (*View).WordLeft,
"SelectWordRight": (*View).SelectWordRight,
"SelectWordLeft": (*View).SelectWordLeft,
"DeleteWordRight": (*View).DeleteWordRight,
"DeleteWordLeft": (*View).DeleteWordLeft,
"SelectToStartOfLine": (*View).SelectToStartOfLine,
"SelectToEndOfLine": (*View).SelectToEndOfLine,
"InsertNewline": (*View).InsertNewline,
"InsertSpace": (*View).InsertSpace,
"Backspace": (*View).Backspace,
"Delete": (*View).Delete,
"InsertTab": (*View).InsertTab,
"Save": (*View).Save,
"SaveAll": (*View).SaveAll,
"SaveAs": (*View).SaveAs,
"Find": (*View).Find,
"FindNext": (*View).FindNext,
"FindPrevious": (*View).FindPrevious,
"Center": (*View).Center,
"Undo": (*View).Undo,
"Redo": (*View).Redo,
"Copy": (*View).Copy,
"Cut": (*View).Cut,
"CutLine": (*View).CutLine,
"DuplicateLine": (*View).DuplicateLine,
"DeleteLine": (*View).DeleteLine,
"MoveLinesUp": (*View).MoveLinesUp,
"MoveLinesDown": (*View).MoveLinesDown,
"IndentSelection": (*View).IndentSelection,
"OutdentSelection": (*View).OutdentSelection,
"OutdentLine": (*View).OutdentLine,
"Paste": (*View).Paste,
"PastePrimary": (*View).PastePrimary,
"SelectAll": (*View).SelectAll,
"OpenFile": (*View).OpenFile,
"Start": (*View).Start,
"End": (*View).End,
"PageUp": (*View).PageUp,
"PageDown": (*View).PageDown,
"HalfPageUp": (*View).HalfPageUp,
"HalfPageDown": (*View).HalfPageDown,
"StartOfLine": (*View).StartOfLine,
"EndOfLine": (*View).EndOfLine,
"ToggleHelp": (*View).ToggleHelp,
"ToggleRuler": (*View).ToggleRuler,
"JumpLine": (*View).JumpLine,
"ClearStatus": (*View).ClearStatus,
"ShellMode": (*View).ShellMode,
"CommandMode": (*View).CommandMode,
"Escape": (*View).Escape,
"Quit": (*View).Quit,
"QuitAll": (*View).QuitAll,
"AddTab": (*View).AddTab,
"PreviousTab": (*View).PreviousTab,
"NextTab": (*View).NextTab,
"NextSplit": (*View).NextSplit,
"PreviousSplit": (*View).PreviousSplit,
"Unsplit": (*View).Unsplit,
"VSplit": (*View).VSplitBinding,
"HSplit": (*View).HSplitBinding,
"ToggleMacro": (*View).ToggleMacro,
"PlayMacro": (*View).PlayMacro,
"Suspend": (*View).Suspend,
"CursorUp": (*View).CursorUp,
"CursorDown": (*View).CursorDown,
"CursorPageUp": (*View).CursorPageUp,
"CursorPageDown": (*View).CursorPageDown,
"CursorLeft": (*View).CursorLeft,
"CursorRight": (*View).CursorRight,
"CursorStart": (*View).CursorStart,
"CursorEnd": (*View).CursorEnd,
"SelectToStart": (*View).SelectToStart,
"SelectToEnd": (*View).SelectToEnd,
"SelectUp": (*View).SelectUp,
"SelectDown": (*View).SelectDown,
"SelectLeft": (*View).SelectLeft,
"SelectRight": (*View).SelectRight,
"WordRight": (*View).WordRight,
"WordLeft": (*View).WordLeft,
"SelectWordRight": (*View).SelectWordRight,
"SelectWordLeft": (*View).SelectWordLeft,
"DeleteWordRight": (*View).DeleteWordRight,
"DeleteWordLeft": (*View).DeleteWordLeft,
"SelectToStartOfLine": (*View).SelectToStartOfLine,
"SelectToEndOfLine": (*View).SelectToEndOfLine,
"InsertNewline": (*View).InsertNewline,
"InsertSpace": (*View).InsertSpace,
"Backspace": (*View).Backspace,
"Delete": (*View).Delete,
"InsertTab": (*View).InsertTab,
"Save": (*View).Save,
"SaveAll": (*View).SaveAll,
"SaveAs": (*View).SaveAs,
"Find": (*View).Find,
"FindNext": (*View).FindNext,
"FindPrevious": (*View).FindPrevious,
"Center": (*View).Center,
"Undo": (*View).Undo,
"Redo": (*View).Redo,
"Copy": (*View).Copy,
"Cut": (*View).Cut,
"CutLine": (*View).CutLine,
"DuplicateLine": (*View).DuplicateLine,
"DeleteLine": (*View).DeleteLine,
"MoveLinesUp": (*View).MoveLinesUp,
"MoveLinesDown": (*View).MoveLinesDown,
"IndentSelection": (*View).IndentSelection,
"OutdentSelection": (*View).OutdentSelection,
"OutdentLine": (*View).OutdentLine,
"Paste": (*View).Paste,
"PastePrimary": (*View).PastePrimary,
"SelectAll": (*View).SelectAll,
"OpenFile": (*View).OpenFile,
"Start": (*View).Start,
"End": (*View).End,
"PageUp": (*View).PageUp,
"PageDown": (*View).PageDown,
"HalfPageUp": (*View).HalfPageUp,
"HalfPageDown": (*View).HalfPageDown,
"StartOfLine": (*View).StartOfLine,
"EndOfLine": (*View).EndOfLine,
"ToggleHelp": (*View).ToggleHelp,
"ToggleRuler": (*View).ToggleRuler,
"JumpLine": (*View).JumpLine,
"ClearStatus": (*View).ClearStatus,
"ShellMode": (*View).ShellMode,
"CommandMode": (*View).CommandMode,
"Escape": (*View).Escape,
"Quit": (*View).Quit,
"QuitAll": (*View).QuitAll,
"AddTab": (*View).AddTab,
"PreviousTab": (*View).PreviousTab,
"NextTab": (*View).NextTab,
"NextSplit": (*View).NextSplit,
"PreviousSplit": (*View).PreviousSplit,
"Unsplit": (*View).Unsplit,
"VSplit": (*View).VSplitBinding,
"HSplit": (*View).HSplitBinding,
"ToggleMacro": (*View).ToggleMacro,
"PlayMacro": (*View).PlayMacro,
"Suspend": (*View).Suspend,
"ScrollUp": (*View).ScrollUpAction,
"ScrollDown": (*View).ScrollDownAction,
"SpawnMultiCursor": (*View).SpawnMultiCursor,
"RemoveMultiCursor": (*View).RemoveMultiCursor,
"RemoveAllMultiCursors": (*View).RemoveAllMultiCursors,
"SkipMultiCursor": (*View).SkipMultiCursor,
// This was changed to InsertNewline but I don't want to break backwards compatibility
"InsertEnter": (*View).InsertNewline,
}
var bindingMouse = map[string]tcell.ButtonMask{
"MouseLeft": tcell.Button1,
"MouseMiddle": tcell.Button2,
"MouseRight": tcell.Button3,
"MouseWheelUp": tcell.WheelUp,
"MouseWheelDown": tcell.WheelDown,
"MouseWheelLeft": tcell.WheelLeft,
"MouseWheelRight": tcell.WheelRight,
}
var bindingKeys = map[string]tcell.Key{
"Up": tcell.KeyUp,
"Down": tcell.KeyDown,
@@ -215,6 +237,8 @@ var bindingKeys = map[string]tcell.Key{
"CtrlRightSq": tcell.KeyCtrlRightSq,
"CtrlCarat": tcell.KeyCtrlCarat,
"CtrlUnderscore": tcell.KeyCtrlUnderscore,
"CtrlPageUp": tcell.KeyCtrlPgUp,
"CtrlPageDown": tcell.KeyCtrlPgDn,
"Tab": tcell.KeyTab,
"Esc": tcell.KeyEsc,
"Escape": tcell.KeyEscape,
@@ -230,12 +254,14 @@ var bindingKeys = map[string]tcell.Key{
type Key struct {
keyCode tcell.Key
modifiers tcell.ModMask
buttons tcell.ButtonMask
r rune
}
// InitBindings initializes the keybindings for micro
func InitBindings() {
bindings = make(map[Key][]func(*View, bool) bool)
mouseBindings = make(map[Key][]func(*View, bool, *tcell.EventMouse) bool)
var parsed map[string]string
defaults := DefaultBindings()
@@ -301,6 +327,7 @@ modSearch:
return Key{
keyCode: code,
modifiers: modifiers,
buttons: -1,
r: 0,
}, true
}
@@ -311,6 +338,16 @@ modSearch:
return Key{
keyCode: code,
modifiers: modifiers,
buttons: -1,
r: 0,
}, true
}
// See if we can find the key in bindingMouse
if code, ok := bindingMouse[k]; ok {
return Key{
modifiers: modifiers,
buttons: code,
r: 0,
}, true
}
@@ -320,12 +357,13 @@ modSearch:
return Key{
keyCode: tcell.KeyRune,
modifiers: modifiers,
buttons: -1,
r: rune(k[0]),
}, true
}
// We don't know what happened.
return Key{}, false
return Key{buttons: -1}, false
}
// findAction will find 'action' using string 'v'
@@ -339,6 +377,16 @@ func findAction(v string) (action func(*View, bool) bool) {
return action
}
func findMouseAction(v string) func(*View, bool, *tcell.EventMouse) bool {
action, ok := mouseBindingActions[v]
if !ok {
// If the user seems to be binding a function that doesn't exist
// We hope that it's a lua function that exists and bind it to that
action = LuaFunctionMouseBinding(v)
}
return action
}
// BindKey takes a key and an action and binds the two together
func BindKey(k, v string) {
key, ok := findKey(k)
@@ -356,18 +404,31 @@ func BindKey(k, v string) {
actionNames := strings.Split(v, ",")
if actionNames[0] == "UnbindKey" {
delete(bindings, key)
delete(mouseBindings, key)
if len(actionNames) == 1 {
actionNames = make([]string, 0, 0)
} else {
actionNames = append(actionNames[:0], actionNames[1:]...)
return
}
actionNames = append(actionNames[:0], actionNames[1:]...)
}
actions := make([]func(*View, bool) bool, 0, len(actionNames))
mouseActions := make([]func(*View, bool, *tcell.EventMouse) bool, 0, len(actionNames))
for _, actionName := range actionNames {
actions = append(actions, findAction(actionName))
if strings.HasPrefix(actionName, "Mouse") {
mouseActions = append(mouseActions, findMouseAction(actionName))
} else {
actions = append(actions, findAction(actionName))
}
}
bindings[key] = actions
if len(actions) > 0 {
// Can't have a binding be both mouse and normal
delete(mouseBindings, key)
bindings[key] = actions
} else if len(mouseActions) > 0 {
// Can't have a binding be both mouse and normal
delete(bindings, key)
mouseBindings[key] = mouseActions
}
}
// DefaultBindings returns a map containing micro's default keybindings
@@ -426,6 +487,8 @@ func DefaultBindings() map[string]string {
"CtrlEnd": "CursorEnd",
"PageUp": "CursorPageUp",
"PageDown": "CursorPageDown",
"CtrlPageUp": "PreviousTab",
"CtrlPageDown": "NextTab",
"CtrlG": "ToggleHelp",
"CtrlR": "ToggleRuler",
"CtrlL": "JumpLine",
@@ -442,8 +505,8 @@ func DefaultBindings() map[string]string {
"Alt-b": "WordLeft",
"Alt-a": "StartOfLine",
"Alt-e": "EndOfLine",
"Alt-p": "CursorUp",
"Alt-n": "CursorDown",
// "Alt-p": "CursorUp",
// "Alt-n": "CursorDown",
// Integration with file managers
"F1": "ToggleHelp",
@@ -453,5 +516,17 @@ func DefaultBindings() map[string]string {
"F7": "Find",
"F10": "Quit",
"Esc": "Escape",
// Mouse bindings
"MouseWheelUp": "ScrollUp",
"MouseWheelDown": "ScrollDown",
"MouseLeft": "MousePress",
"MouseMiddle": "PastePrimary",
"Ctrl-MouseLeft": "MouseMultiCursor",
"Alt-n": "SpawnMultiCursor",
"Alt-p": "RemoveMultiCursor",
"Alt-c": "RemoveAllMultiCursors",
"Alt-x": "SkipMultiCursor",
}
}

View File

@@ -28,7 +28,9 @@ type Buffer struct {
// This stores all the text in the buffer as an array of lines
*LineArray
Cursor Cursor
Cursor Cursor
cursors []*Cursor // for multiple cursors
curCursor int // the current cursor
// Path to the file on disk
Path string
@@ -169,6 +171,8 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
file.Close()
}
b.cursors = []*Cursor{&b.Cursor}
return b
}
@@ -204,7 +208,7 @@ func (b *Buffer) UpdateRules() {
}
ft := b.Settings["filetype"].(string)
if ft == "Unknown" || ft == "" {
if (ft == "Unknown" || ft == "") && !rehighlight {
if highlight.MatchFiletype(ftdetect, b.Path, b.lines[0].data) {
header := new(highlight.Header)
header.FileType = file.FileType
@@ -217,7 +221,7 @@ func (b *Buffer) UpdateRules() {
rehighlight = true
}
} else {
if file.FileType == ft {
if file.FileType == ft && !rehighlight {
header := new(highlight.Header)
header.FileType = file.FileType
header.FtDetect = ftdetect
@@ -305,6 +309,30 @@ func (b *Buffer) Update() {
b.NumLines = len(b.lines)
}
func (b *Buffer) MergeCursors() {
var cursors []*Cursor
for i := 0; i < len(b.cursors); i++ {
c1 := b.cursors[i]
if c1 != nil {
for j := 0; j < len(b.cursors); j++ {
c2 := b.cursors[j]
if c2 != nil && i != j && c1.Loc == c2.Loc {
b.cursors[j] = nil
}
}
cursors = append(cursors, c1)
}
}
b.cursors = cursors
}
func (b *Buffer) UpdateCursors() {
for i, c := range b.cursors {
c.Num = i
}
}
// Save saves the buffer to its default path
func (b *Buffer) Save() error {
return b.SaveAs(b.Path)
@@ -375,17 +403,9 @@ func (b *Buffer) SaveAsWithSudo(filename string) error {
b.UpdateRules()
b.Path = filename
// The user may have already used sudo in which case we won't need the password
// It's a bit nicer for them if they don't have to enter the password every time
_, err := RunShellCommand("sudo -v")
needPassword := err != nil
// If we need the password, we have to close the screen and ask using the shell
if needPassword {
// Shut down the screen because we're going to interact directly with the shell
screen.Fini()
screen = nil
}
// Shut down the screen because we're going to interact directly with the shell
screen.Fini()
screen = nil
// Set up everything for the command
cmd := exec.Command("sudo", "tee", filename)
@@ -403,13 +423,10 @@ func (b *Buffer) SaveAsWithSudo(filename string) error {
// Start the command
cmd.Start()
err = cmd.Wait()
err := cmd.Wait()
// If we needed the password, we closed the screen, so we have to initialize it again
if needPassword {
// Start the screen back up
InitScreen()
}
// Start the screen back up
InitScreen()
if err == nil {
b.IsModified = false
b.ModTime, _ = GetModTime(filename)

View File

@@ -35,27 +35,28 @@ var commandActions map[string]func([]string)
func init() {
commandActions = map[string]func([]string){
"Set": Set,
"SetLocal": SetLocal,
"Show": Show,
"Run": Run,
"Bind": Bind,
"Quit": Quit,
"Save": Save,
"Replace": Replace,
"VSplit": VSplit,
"HSplit": HSplit,
"Tab": NewTab,
"Help": Help,
"Eval": Eval,
"ToggleLog": ToggleLog,
"Plugin": PluginCmd,
"Reload": Reload,
"Cd": Cd,
"Pwd": Pwd,
"Open": Open,
"TabSwitch": TabSwitch,
"MemUsage": MemUsage,
"Set": Set,
"SetLocal": SetLocal,
"Show": Show,
"Run": Run,
"Bind": Bind,
"Quit": Quit,
"Save": Save,
"Replace": Replace,
"ReplaceAll": ReplaceAll,
"VSplit": VSplit,
"HSplit": HSplit,
"Tab": NewTab,
"Help": Help,
"Eval": Eval,
"ToggleLog": ToggleLog,
"Plugin": PluginCmd,
"Reload": Reload,
"Cd": Cd,
"Pwd": Pwd,
"Open": Open,
"TabSwitch": TabSwitch,
"MemUsage": MemUsage,
}
}
@@ -89,27 +90,28 @@ func MakeCommand(name, function string, completions ...Completion) {
// DefaultCommands returns a map containing micro's default commands
func DefaultCommands() map[string]StrCommand {
return map[string]StrCommand{
"set": {"Set", []Completion{OptionCompletion, NoCompletion}},
"setlocal": {"SetLocal", []Completion{OptionCompletion, NoCompletion}},
"show": {"Show", []Completion{OptionCompletion, NoCompletion}},
"bind": {"Bind", []Completion{NoCompletion}},
"run": {"Run", []Completion{NoCompletion}},
"quit": {"Quit", []Completion{NoCompletion}},
"save": {"Save", []Completion{NoCompletion}},
"replace": {"Replace", []Completion{NoCompletion}},
"vsplit": {"VSplit", []Completion{FileCompletion, NoCompletion}},
"hsplit": {"HSplit", []Completion{FileCompletion, NoCompletion}},
"tab": {"Tab", []Completion{FileCompletion, NoCompletion}},
"help": {"Help", []Completion{HelpCompletion, NoCompletion}},
"eval": {"Eval", []Completion{NoCompletion}},
"log": {"ToggleLog", []Completion{NoCompletion}},
"plugin": {"Plugin", []Completion{PluginCmdCompletion, PluginNameCompletion}},
"reload": {"Reload", []Completion{NoCompletion}},
"cd": {"Cd", []Completion{FileCompletion}},
"pwd": {"Pwd", []Completion{NoCompletion}},
"open": {"Open", []Completion{FileCompletion}},
"tabswitch": {"TabSwitch", []Completion{NoCompletion}},
"memusage": {"MemUsage", []Completion{NoCompletion}},
"set": {"Set", []Completion{OptionCompletion, NoCompletion}},
"setlocal": {"SetLocal", []Completion{OptionCompletion, NoCompletion}},
"show": {"Show", []Completion{OptionCompletion, NoCompletion}},
"bind": {"Bind", []Completion{NoCompletion}},
"run": {"Run", []Completion{NoCompletion}},
"quit": {"Quit", []Completion{NoCompletion}},
"save": {"Save", []Completion{NoCompletion}},
"replace": {"Replace", []Completion{NoCompletion}},
"replaceall": {"ReplaceAll", []Completion{NoCompletion}},
"vsplit": {"VSplit", []Completion{FileCompletion, NoCompletion}},
"hsplit": {"HSplit", []Completion{FileCompletion, NoCompletion}},
"tab": {"Tab", []Completion{FileCompletion, NoCompletion}},
"help": {"Help", []Completion{HelpCompletion, NoCompletion}},
"eval": {"Eval", []Completion{NoCompletion}},
"log": {"ToggleLog", []Completion{NoCompletion}},
"plugin": {"Plugin", []Completion{PluginCmdCompletion, PluginNameCompletion}},
"reload": {"Reload", []Completion{NoCompletion}},
"cd": {"Cd", []Completion{FileCompletion}},
"pwd": {"Pwd", []Completion{NoCompletion}},
"open": {"Open", []Completion{FileCompletion}},
"tabswitch": {"TabSwitch", []Completion{NoCompletion}},
"memusage": {"MemUsage", []Completion{NoCompletion}},
}
}
@@ -506,16 +508,21 @@ func Save(args []string) {
// Replace runs search and replace
func Replace(args []string) {
if len(args) < 2 {
if len(args) < 2 || len(args) > 3 {
// We need to find both a search and replace expression
messenger.Error("Invalid replace statement: " + strings.Join(args, " "))
return
}
var flags string
allAtOnce := false
if len(args) == 3 {
// The user included some flags
flags = args[2]
// user added -a flag
if args[2] == "-a" {
allAtOnce = true
} else {
messenger.Error("Invalid replace flag: " + args[2])
return
}
}
search := string(args[0])
@@ -531,7 +538,32 @@ func Replace(args []string) {
view := CurView()
found := 0
if strings.Contains(flags, "c") {
replaceAll := func() {
var deltas []Delta
deltaXOffset := Count(replace) - Count(search)
for i := 0; i < view.Buf.LinesNum(); i++ {
matches := regex.FindAllIndex(view.Buf.lines[i].data, -1)
str := string(view.Buf.lines[i].data)
if matches != nil {
xOffset := 0
for _, m := range matches {
from := Loc{runePos(m[0], str) + xOffset, i}
to := Loc{runePos(m[1], str) + xOffset, i}
xOffset += deltaXOffset
deltas = append(deltas, Delta{replace, from, to})
found++
}
}
}
view.Buf.MultipleReplace(deltas)
}
if allAtOnce {
replaceAll()
} else {
for {
// The 'check' flag was used
Search(search, view, true)
@@ -540,7 +572,7 @@ func Replace(args []string) {
}
view.Relocate()
RedrawAll()
choice, canceled := messenger.YesNoPrompt("Perform replacement? (y,n)")
choice, canceled := messenger.LetterPrompt("Perform replacement? (y,n,a)", 'y', 'n', 'a')
if canceled {
if view.Cursor.HasSelection() {
view.Cursor.Loc = view.Cursor.CurSelection[0]
@@ -548,42 +580,27 @@ func Replace(args []string) {
}
messenger.Reset()
break
}
if choice {
} else if choice == 'a' {
if view.Cursor.HasSelection() {
view.Cursor.Loc = view.Cursor.CurSelection[0]
view.Cursor.ResetSelection()
}
messenger.Reset()
replaceAll()
break
} else if choice == 'y' {
view.Cursor.DeleteSelection()
view.Buf.Insert(view.Cursor.Loc, replace)
view.Cursor.ResetSelection()
messenger.Reset()
found++
}
if view.Cursor.HasSelection() {
searchStart = view.Cursor.CurSelection[1]
} else {
if view.Cursor.HasSelection() {
searchStart = ToCharPos(view.Cursor.CurSelection[1], view.Buf)
} else {
searchStart = ToCharPos(view.Cursor.Loc, view.Buf)
}
continue
searchStart = view.Cursor.Loc
}
}
} else {
// var deltas []Delta
for i := 0; i < view.Buf.LinesNum(); i++ {
// view.Buf.lines[i].data = regex.ReplaceAll(view.Buf.lines[i].data, []byte(replace))
for {
m := regex.FindIndex(view.Buf.lines[i].data)
if m != nil {
from := Loc{m[0], i}
to := Loc{m[1], i}
// deltas = append(deltas, Delta{replace, from, to})
view.Buf.Replace(from, to, replace)
found++
} else {
break
}
}
}
// view.Buf.MultipleReplace(deltas)
}
view.Cursor.Relocate()
@@ -596,6 +613,12 @@ func Replace(args []string) {
}
}
// ReplaceAll replaces search term all at once
func ReplaceAll(args []string) {
// aliased to Replace command
Replace(append(args, "-a"))
}
// RunShellCommand executes a shell command and returns the output/error
func RunShellCommand(input string) (string, error) {
inputCmd := SplitCommandArgs(input)[0]

View File

@@ -1,6 +1,8 @@
package main
import "github.com/zyedidia/clipboard"
import (
"github.com/zyedidia/clipboard"
)
// The Cursor struct stores the location of the cursor in the view
// The complicated part about the cursor is storing its location.
@@ -21,6 +23,9 @@ type Cursor struct {
// This is used for line and word selection where it is necessary
// to know what the original selection was
OrigSelection [2]Loc
// Which cursor index is this (for multiple cursors)
Num int
}
// Goto puts the cursor at the given cursor's location and gives the current cursor its selection too
@@ -334,9 +339,22 @@ func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int {
func (c *Cursor) GetVisualX() int {
runes := []rune(c.buf.Line(c.Y))
tabSize := int(c.buf.Settings["tabsize"].(float64))
if c.X > len(runes) {
c.X = len(runes) - 1
}
if c.X < 0 {
c.X = 0
}
return StringWidth(string(runes[:c.X]), tabSize)
}
// StoreVisualX stores the current visual x value in the cursor
func (c *Cursor) StoreVisualX() {
c.LastVisualX = c.GetVisualX()
}
// Relocate makes sure that the cursor is inside the bounds of the buffer
// If it isn't, it moves it to be within the buffer's lines
func (c *Cursor) Relocate() {

View File

@@ -11,11 +11,11 @@ import (
const (
// Opposite and undoing events must have opposite values
// TextEventInsert repreasents an insertion event
// TextEventInsert represents an insertion event
TextEventInsert = 1
// TextEventRemove represents a deletion event
TextEventRemove = -1
// TextEventReplace represents a replace event
TextEventReplace = 0
)
@@ -97,30 +97,65 @@ func (eh *EventHandler) ApplyDiff(new string) {
// Insert creates an insert text event and executes it
func (eh *EventHandler) Insert(start Loc, text string) {
e := &TextEvent{
C: eh.buf.Cursor,
C: *eh.buf.cursors[eh.buf.curCursor],
EventType: TextEventInsert,
Deltas: []Delta{Delta{text, start, Loc{0, 0}}},
Time: time.Now(),
}
eh.Execute(e)
e.Deltas[0].End = start.Move(Count(text), eh.buf)
end := e.Deltas[0].End
for _, c := range eh.buf.cursors {
move := func(loc Loc) Loc {
if start.Y != end.Y && loc.GreaterThan(start) {
loc.Y += end.Y - start.Y
} else if loc.Y == start.Y && loc.GreaterEqual(start) {
loc = loc.Move(Count(text), eh.buf)
}
return loc
}
c.Loc = move(c.Loc)
c.CurSelection[0] = move(c.CurSelection[0])
c.CurSelection[1] = move(c.CurSelection[1])
c.OrigSelection[0] = move(c.OrigSelection[0])
c.OrigSelection[1] = move(c.OrigSelection[1])
c.LastVisualX = c.GetVisualX()
}
}
// Remove creates a remove text event and executes it
func (eh *EventHandler) Remove(start, end Loc) {
e := &TextEvent{
C: eh.buf.Cursor,
C: *eh.buf.cursors[eh.buf.curCursor],
EventType: TextEventRemove,
Deltas: []Delta{Delta{"", start, end}},
Time: time.Now(),
}
eh.Execute(e)
for _, c := range eh.buf.cursors {
move := func(loc Loc) Loc {
if start.Y != end.Y && loc.GreaterThan(end) {
loc.Y -= end.Y - start.Y
} else if loc.Y == end.Y && loc.GreaterEqual(end) {
loc = loc.Move(-Diff(start, end, eh.buf), eh.buf)
}
return loc
}
c.Loc = move(c.Loc)
c.CurSelection[0] = move(c.CurSelection[0])
c.CurSelection[1] = move(c.CurSelection[1])
c.OrigSelection[0] = move(c.OrigSelection[0])
c.OrigSelection[1] = move(c.OrigSelection[1])
c.LastVisualX = c.GetVisualX()
}
}
// Multiple creates an multiple insertions executes them
// MultipleReplace creates an multiple insertions executes them
func (eh *EventHandler) MultipleReplace(deltas []Delta) {
e := &TextEvent{
C: eh.buf.Cursor,
C: *eh.buf.cursors[eh.buf.curCursor],
EventType: TextEventReplace,
Deltas: deltas,
Time: time.Now(),
@@ -195,8 +230,12 @@ func (eh *EventHandler) UndoOneEvent() {
// Set the cursor in the right place
teCursor := t.C
t.C = eh.buf.Cursor
eh.buf.Cursor.Goto(teCursor)
if teCursor.Num >= 0 && teCursor.Num < len(eh.buf.cursors) {
t.C = *eh.buf.cursors[teCursor.Num]
eh.buf.cursors[teCursor.Num].Goto(teCursor)
} else {
teCursor.Num = -1
}
// Push it to the redo stack
eh.RedoStack.Push(t)
@@ -238,8 +277,12 @@ func (eh *EventHandler) RedoOneEvent() {
UndoTextEvent(t, eh.buf)
teCursor := t.C
t.C = eh.buf.Cursor
eh.buf.Cursor.Goto(teCursor)
if teCursor.Num >= 0 && teCursor.Num < len(eh.buf.cursors) {
t.C = *eh.buf.cursors[teCursor.Num]
eh.buf.cursors[teCursor.Num].Goto(teCursor)
} else {
teCursor.Num = -1
}
eh.UndoStack.Push(t)
}

View File

@@ -1,6 +1,7 @@
package highlight
import (
"errors"
"fmt"
"regexp"
@@ -82,6 +83,16 @@ func init() {
}
func ParseFtDetect(file *File) (r [2]*regexp.Regexp, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("pkg: %v", r)
}
}
}()
rules := file.yamlSrc
loaded := 0
@@ -112,14 +123,22 @@ func ParseFtDetect(file *File) (r [2]*regexp.Regexp, err error) {
}
}
if loaded == 0 {
return r, errors.New("No detect regexes found")
}
return r, err
}
func ParseFile(input []byte) (f *File, err error) {
// This is just so if we have an error, we can exit cleanly and return the parse error to the user
defer func() {
if e := recover(); e != nil {
err = e.(error)
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("pkg: %v", r)
}
}
}()
@@ -147,8 +166,12 @@ func ParseFile(input []byte) (f *File, err error) {
func ParseDef(f *File, header *Header) (s *Def, err error) {
// This is just so if we have an error, we can exit cleanly and return the parse error to the user
defer func() {
if e := recover(); e != nil {
err = e.(error)
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("pkg: %v", r)
}
}
}()
@@ -211,8 +234,17 @@ func resolveIncludesInRegion(files []*File, region *region) {
}
}
func parseRules(input []interface{}, curRegion *region) (*rules, error) {
rules := new(rules)
func parseRules(input []interface{}, curRegion *region) (ru *rules, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("pkg: %v", r)
}
}
}()
ru = new(rules)
for _, v := range input {
rule := v.(map[interface{}]interface{})
@@ -222,7 +254,7 @@ func parseRules(input []interface{}, curRegion *region) (*rules, error) {
switch object := val.(type) {
case string:
if k == "include" {
rules.includes = append(rules.includes, object)
ru.includes = append(ru.includes, object)
} else {
// Pattern
r, err := regexp.Compile(object)
@@ -236,7 +268,7 @@ func parseRules(input []interface{}, curRegion *region) (*rules, error) {
Groups[groupStr] = numGroups
}
groupNum := Groups[groupStr]
rules.patterns = append(rules.patterns, &pattern{groupNum, r})
ru.patterns = append(ru.patterns, &pattern{groupNum, r})
}
case map[interface{}]interface{}:
// region
@@ -244,35 +276,43 @@ func parseRules(input []interface{}, curRegion *region) (*rules, error) {
if err != nil {
return nil, err
}
rules.regions = append(rules.regions, region)
ru.regions = append(ru.regions, region)
default:
return nil, fmt.Errorf("Bad type %T", object)
}
}
}
return rules, nil
return ru, nil
}
func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegion *region) (*region, error) {
var err error
func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegion *region) (r *region, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("pkg: %v", r)
}
}
}()
region := new(region)
r = new(region)
if _, ok := Groups[group]; !ok {
numGroups++
Groups[group] = numGroups
}
groupNum := Groups[group]
region.group = groupNum
region.parent = prevRegion
r.group = groupNum
r.parent = prevRegion
region.start, err = regexp.Compile(regionInfo["start"].(string))
r.start, err = regexp.Compile(regionInfo["start"].(string))
if err != nil {
return nil, err
}
region.end, err = regexp.Compile(regionInfo["end"].(string))
r.end, err = regexp.Compile(regionInfo["end"].(string))
if err != nil {
return nil, err
@@ -280,7 +320,7 @@ func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegio
// skip is optional
if _, ok := regionInfo["skip"]; ok {
region.skip, err = regexp.Compile(regionInfo["skip"].(string))
r.skip, err = regexp.Compile(regionInfo["skip"].(string))
if err != nil {
return nil, err
@@ -295,20 +335,20 @@ func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegio
Groups[groupStr] = numGroups
}
groupNum := Groups[groupStr]
region.limitGroup = groupNum
r.limitGroup = groupNum
if err != nil {
return nil, err
}
} else {
region.limitGroup = region.group
r.limitGroup = r.group
}
region.rules, err = parseRules(regionInfo["rules"].([]interface{}), region)
r.rules, err = parseRules(regionInfo["rules"].([]interface{}), r)
if err != nil {
return nil, err
}
return region, nil
return r, nil
}

View File

@@ -54,6 +54,28 @@ type Loc struct {
X, Y int
}
func Diff(a, b Loc, buf *Buffer) int {
if a.Y == b.Y {
if a.X > b.X {
return a.X - b.X
}
return b.X - a.X
}
// Make sure a is guaranteed to be less than b
if b.LessThan(a) {
a, b = b, a
}
loc := 0
for i := a.Y + 1; i < b.Y; i++ {
// + 1 for the newline
loc += Count(buf.Line(i)) + 1
}
loc += Count(buf.Line(a.Y)) - a.X + b.X + 1
return loc
}
// LessThan returns true if b is smaller
func (l Loc) LessThan(b Loc) bool {
if l.Y < b.Y {

View File

@@ -9,6 +9,7 @@ import (
"github.com/zyedidia/clipboard"
"github.com/zyedidia/tcell"
"github.com/mattn/go-runewidth"
)
// TermMessage sends a message to the user in the terminal. This usually occurs before
@@ -463,8 +464,10 @@ func (m *Messenger) Display() {
if m.hasMessage {
if m.hasPrompt || globalSettings["infobar"].(bool) {
runes := []rune(m.message + m.response)
posx := 0
for x := 0; x < len(runes); x++ {
screen.SetContent(x, h-1, runes[x], nil, m.style)
screen.SetContent(posx, h-1, runes[x], nil, m.style)
posx += runewidth.RuneWidth(runes[x])
}
}
}

View File

@@ -183,6 +183,10 @@ func InitScreen() {
screen, err = tcell.NewScreen()
if err != nil {
fmt.Println(err)
if err == tcell.ErrTermNotFound {
fmt.Println("Micro does not recognize your terminal:", oldTerm)
fmt.Println("Please go to https://github.com/zyedidia/mkinfo to read about how to fix this problem (it should be easy to fix).")
}
os.Exit(1)
}
if err = screen.Init(); err != nil {
@@ -248,6 +252,7 @@ func main() {
flag.Usage = func() {
fmt.Println("Usage: micro [OPTIONS] [FILE]...")
fmt.Print("Micro's options can be set via command line arguments for quick adjustments. For real configuration, please use the bindings.json file (see 'help options').\n\n")
flag.CommandLine.SetOutput(os.Stdout)
flag.PrintDefaults()
}
@@ -403,7 +408,9 @@ func main() {
// Here is the event loop which runs in a separate thread
go func() {
for {
events <- screen.PollEvent()
if screen != nil {
events <- screen.PollEvent()
}
}
}()

View File

@@ -7,6 +7,7 @@ import (
"strings"
"github.com/yuin/gopher-lua"
"github.com/zyedidia/tcell"
"layeh.com/gopher-luar"
)
@@ -60,6 +61,16 @@ func LuaFunctionBinding(function string) func(*View, bool) bool {
}
}
func LuaFunctionMouseBinding(function string) func(*View, bool, *tcell.EventMouse) bool {
return func(v *View, _ bool, e *tcell.EventMouse) bool {
_, err := Call(function, e)
if err != nil {
TermMessage(err)
}
return false
}
}
func unpack(old []string) []interface{} {
new := make([]interface{}, len(old))
for i, v := range old {

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,7 @@ package main
import (
"regexp"
"strings"
"github.com/zyedidia/tcell"
)
@@ -11,7 +12,7 @@ var (
lastSearch string
// Where should we start the search down from (or up from)
searchStart int
searchStart Loc
// Is there currently a search in progress
searching bool
@@ -43,7 +44,7 @@ func EndSearch() {
}
}
// exit the search mode, reset active search phrase, and clear status bar
// ExitSearch exits the search mode, reset active search phrase, and clear status bar
func ExitSearch(v *View) {
lastSearch = ""
searching = false
@@ -91,6 +92,62 @@ func HandleSearchEvent(event tcell.Event, v *View) {
return
}
func searchDown(r *regexp.Regexp, v *View, start, end Loc) bool {
for i := start.Y; i <= end.Y; i++ {
var l []byte
var charPos int
if i == start.Y {
runes := []rune(string(v.Buf.lines[i].data))
l = []byte(string(runes[start.X:]))
charPos = start.X
if strings.Contains(r.String(), "^") && start.X != 0 {
continue
}
} else {
l = v.Buf.lines[i].data
}
match := r.FindIndex(l)
if match != nil {
v.Cursor.SetSelectionStart(Loc{charPos + runePos(match[0], string(l)), i})
v.Cursor.SetSelectionEnd(Loc{charPos + runePos(match[1], string(l)), i})
v.Cursor.Loc = v.Cursor.CurSelection[1]
return true
}
}
return false
}
func searchUp(r *regexp.Regexp, v *View, start, end Loc) bool {
for i := start.Y; i >= end.Y; i-- {
var l []byte
if i == start.Y {
runes := []rune(string(v.Buf.lines[i].data))
l = []byte(string(runes[:start.X]))
if strings.Contains(r.String(), "$") && start.X != Count(string(l)) {
continue
}
} else {
l = v.Buf.lines[i].data
}
match := r.FindIndex(l)
if match != nil {
v.Cursor.SetSelectionStart(Loc{runePos(match[0], string(l)), i})
v.Cursor.SetSelectionEnd(Loc{runePos(match[1], string(l)), i})
v.Cursor.Loc = v.Cursor.CurSelection[1]
return true
}
}
return false
}
// Search searches in the view for the given regex. The down bool
// specifies whether it should search down from the searchStart position
// or up from there
@@ -98,15 +155,6 @@ func Search(searchStr string, v *View, down bool) {
if searchStr == "" {
return
}
var str string
var charPos int
text := v.Buf.String()
if down {
str = string([]rune(text)[searchStart:])
charPos = searchStart
} else {
str = string([]rune(text)[:searchStart])
}
r, err := regexp.Compile(searchStr)
if v.Buf.Settings["ignorecase"].(bool) {
r, err = regexp.Compile("(?i)" + searchStr)
@@ -114,37 +162,22 @@ func Search(searchStr string, v *View, down bool) {
if err != nil {
return
}
matches := r.FindAllStringIndex(str, -1)
var match []int
if matches == nil {
// Search the entire buffer now
matches = r.FindAllStringIndex(text, -1)
charPos = 0
if matches == nil {
v.Cursor.ResetSelection()
return
}
if !down {
match = matches[len(matches)-1]
} else {
match = matches[0]
var found bool
if down {
found = searchDown(r, v, searchStart, v.Buf.End())
if !found {
found = searchDown(r, v, v.Buf.Start(), searchStart)
}
str = text
}
if !down {
match = matches[len(matches)-1]
} else {
match = matches[0]
found = searchUp(r, v, searchStart, v.Buf.Start())
if !found {
found = searchUp(r, v, v.Buf.End(), searchStart)
}
}
if match[0] == match[1] {
return
if found {
lastSearch = searchStr
} else {
v.Cursor.ResetSelection()
}
v.Cursor.SetSelectionStart(FromCharPos(charPos+runePos(match[0], str), v.Buf))
v.Cursor.SetSelectionEnd(FromCharPos(charPos+runePos(match[1], str), v.Buf))
v.Cursor.Loc = v.Cursor.CurSelection[1]
lastSearch = searchStr
}

View File

@@ -36,11 +36,7 @@ func NumOccurrences(s string, c byte) int {
// Spaces returns a string with n spaces
func Spaces(n int) string {
var str string
for i := 0; i < n; i++ {
str += " "
}
return str
return strings.Repeat(" ", n)
}
// Min takes the min of two ints
@@ -267,11 +263,16 @@ func Abs(n int) int {
return n
}
// FuncName returns the name of a given function object
// FuncName returns the full name of a given function object
func FuncName(i interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}
// ShortFuncName returns the name only of a given function object
func ShortFuncName(i interface{}) string {
return strings.TrimPrefix(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name(), "main.(*View).")
}
// SplitCommandArgs separates multiple command arguments which may be quoted.
// The returned slice contains at least one string
func SplitCommandArgs(input string) []string {

1
cmd/micro/vendor/golang.org/x/net generated vendored Submodule

1
cmd/micro/vendor/golang.org/x/text generated vendored Submodule

1
cmd/micro/vendor/gopkg.in/yaml.v2 generated vendored Submodule

View File

@@ -169,7 +169,7 @@ func (v *View) paste(clip string) {
}
clip = strings.Replace(clip, "\n", "\n"+leadingWS, -1)
v.Buf.Insert(v.Cursor.Loc, clip)
v.Cursor.Loc = v.Cursor.Loc.Move(Count(clip), v.Buf)
// v.Cursor.Loc = v.Cursor.Loc.Move(Count(clip), v.Buf)
v.freshClip = false
messenger.Message("Pasted clipboard")
}
@@ -450,6 +450,40 @@ func (v *View) MoveToMouseClick(x, y int) {
v.Cursor.LastVisualX = v.Cursor.GetVisualX()
}
func (v *View) ExecuteActions(actions []func(*View, bool) bool) bool {
relocate := false
readonlyBindingsList := []string{"Delete", "Insert", "Backspace", "Cut", "Play", "Paste", "Move", "Add", "DuplicateLine", "Macro"}
for _, action := range actions {
readonlyBindingsResult := false
funcName := ShortFuncName(action)
if v.Type.readonly == true {
// check for readonly and if true only let key bindings get called if they do not change the contents.
for _, readonlyBindings := range readonlyBindingsList {
if strings.Contains(funcName, readonlyBindings) {
readonlyBindingsResult = true
}
}
}
if !readonlyBindingsResult {
// call the key binding
relocate = action(v, true) || relocate
// Macro
if funcName != "ToggleMacro" && funcName != "PlayMacro" {
if recordingMacro {
curMacro = append(curMacro, action)
}
}
}
}
return relocate
}
func (v *View) SetCursor(c *Cursor) {
v.Cursor = c
v.Buf.curCursor = c.Num
}
// HandleEvent handles an event passed by the main loop
func (v *View) HandleEvent(event tcell.Event) {
// This bool determines whether the view is relocated at the end of the function
@@ -462,123 +496,104 @@ func (v *View) HandleEvent(event tcell.Event) {
case *tcell.EventKey:
// Check first if input is a key binding, if it is we 'eat' the input and don't insert a rune
isBinding := false
if e.Key() != tcell.KeyRune || e.Modifiers() != 0 {
for key, actions := range bindings {
if e.Key() == key.keyCode {
if e.Key() == tcell.KeyRune {
if e.Rune() != key.r {
continue
}
for key, actions := range bindings {
if e.Key() == key.keyCode {
if e.Key() == tcell.KeyRune {
if e.Rune() != key.r {
continue
}
if e.Modifiers() == key.modifiers {
}
if e.Modifiers() == key.modifiers {
for _, c := range v.Buf.cursors {
v.SetCursor(c)
relocate = false
isBinding = true
for _, action := range actions {
relocate = action(v, true) || relocate
funcName := FuncName(action)
if funcName != "main.(*View).ToggleMacro" && funcName != "main.(*View).PlayMacro" {
if recordingMacro {
curMacro = append(curMacro, action)
}
}
}
break
relocate = v.ExecuteActions(actions) || relocate
}
v.SetCursor(&v.Buf.Cursor)
v.Buf.MergeCursors()
break
}
}
}
if !isBinding && e.Key() == tcell.KeyRune {
// Insert a character
if v.Cursor.HasSelection() {
v.Cursor.DeleteSelection()
v.Cursor.ResetSelection()
}
v.Buf.Insert(v.Cursor.Loc, string(e.Rune()))
v.Cursor.Right()
// Check viewtype if readonly don't insert a rune (readonly help and log view etc.)
if v.Type.readonly == false {
for _, c := range v.Buf.cursors {
v.SetCursor(c)
for pl := range loadedPlugins {
_, err := Call(pl+".onRune", string(e.Rune()), v)
if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
TermMessage(err)
// Insert a character
if v.Cursor.HasSelection() {
v.Cursor.DeleteSelection()
v.Cursor.ResetSelection()
}
v.Buf.Insert(v.Cursor.Loc, string(e.Rune()))
for pl := range loadedPlugins {
_, err := Call(pl+".onRune", string(e.Rune()), v)
if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
TermMessage(err)
}
}
if recordingMacro {
curMacro = append(curMacro, e.Rune())
}
}
}
if recordingMacro {
curMacro = append(curMacro, e.Rune())
v.SetCursor(&v.Buf.Cursor)
}
}
case *tcell.EventPaste:
if !PreActionCall("Paste", v) {
break
// Check viewtype if readonly don't paste (readonly help and log view etc.)
if v.Type.readonly == false {
if !PreActionCall("Paste", v) {
break
}
for _, c := range v.Buf.cursors {
v.SetCursor(c)
v.paste(e.Text())
}
v.SetCursor(&v.Buf.Cursor)
PostActionCall("Paste", v)
}
v.paste(e.Text())
PostActionCall("Paste", v)
case *tcell.EventMouse:
x, y := e.Position()
x -= v.lineNumOffset - v.leftCol + v.x
y += v.Topline - v.y
// Don't relocate for mouse events
relocate = false
button := e.Buttons()
switch button {
case tcell.Button1:
// Left click
if v.mouseReleased {
v.MoveToMouseClick(x, y)
if time.Since(v.lastClickTime)/time.Millisecond < doubleClickThreshold {
if v.doubleClick {
// Triple click
v.lastClickTime = time.Now()
v.tripleClick = true
v.doubleClick = false
v.Cursor.SelectLine()
v.Cursor.CopySelection("primary")
} else {
// Double click
v.lastClickTime = time.Now()
v.doubleClick = true
v.tripleClick = false
v.Cursor.SelectWord()
v.Cursor.CopySelection("primary")
}
} else {
v.doubleClick = false
v.tripleClick = false
v.lastClickTime = time.Now()
v.Cursor.OrigSelection[0] = v.Cursor.Loc
v.Cursor.CurSelection[0] = v.Cursor.Loc
v.Cursor.CurSelection[1] = v.Cursor.Loc
for key, actions := range bindings {
if button == key.buttons && e.Modifiers() == key.modifiers {
for _, c := range v.Buf.cursors {
v.SetCursor(c)
relocate = v.ExecuteActions(actions) || relocate
}
v.mouseReleased = false
} else if !v.mouseReleased {
v.MoveToMouseClick(x, y)
if v.tripleClick {
v.Cursor.AddLineToSelection()
} else if v.doubleClick {
v.Cursor.AddWordToSelection()
} else {
v.Cursor.SetSelectionEnd(v.Cursor.Loc)
v.Cursor.CopySelection("primary")
v.SetCursor(&v.Buf.Cursor)
v.Buf.MergeCursors()
}
}
for key, actions := range mouseBindings {
if button == key.buttons && e.Modifiers() == key.modifiers {
for _, action := range actions {
action(v, true, e)
}
}
case tcell.Button2:
// Middle mouse button was clicked,
// We should paste primary
v.PastePrimary(true)
}
switch button {
case tcell.ButtonNone:
// Mouse event with no click
if !v.mouseReleased {
// Mouse was just released
x, y := e.Position()
x -= v.lineNumOffset - v.leftCol + v.x
y += v.Topline - v.y
// Relocating here isn't really necessary because the cursor will
// be in the right place from the last mouse event
// However, if we are running in a terminal that doesn't support mouse motion
@@ -592,14 +607,6 @@ func (v *View) HandleEvent(event tcell.Event) {
}
v.mouseReleased = true
}
case tcell.WheelUp:
// Scroll up
scrollspeed := int(v.Buf.Settings["scrollspeed"].(float64))
v.ScrollUp(scrollspeed)
case tcell.WheelDown:
// Scroll down
scrollspeed := int(v.Buf.Settings["scrollspeed"].(float64))
v.ScrollDown(scrollspeed)
}
}
@@ -614,6 +621,10 @@ func (v *View) HandleEvent(event tcell.Event) {
}
}
func (v *View) mainCursor() bool {
return v.Buf.curCursor == len(v.Buf.cursors)-1
}
// GutterMessage creates a message in this view's gutter
func (v *View) GutterMessage(section string, lineN int, msg string, kind int) {
lineN--
@@ -840,22 +851,20 @@ func (v *View) DisplayView() {
}
charLoc := char.realLoc
if v.Cursor.HasSelection() &&
(charLoc.GreaterEqual(v.Cursor.CurSelection[0]) && charLoc.LessThan(v.Cursor.CurSelection[1]) ||
charLoc.LessThan(v.Cursor.CurSelection[0]) && charLoc.GreaterEqual(v.Cursor.CurSelection[1])) {
// The current character is selected
lineStyle = defStyle.Reverse(true)
for _, c := range v.Buf.cursors {
v.SetCursor(c)
if v.Cursor.HasSelection() &&
(charLoc.GreaterEqual(v.Cursor.CurSelection[0]) && charLoc.LessThan(v.Cursor.CurSelection[1]) ||
charLoc.LessThan(v.Cursor.CurSelection[0]) && charLoc.GreaterEqual(v.Cursor.CurSelection[1])) {
// The current character is selected
lineStyle = defStyle.Reverse(true)
if style, ok := colorscheme["selection"]; ok {
lineStyle = style
if style, ok := colorscheme["selection"]; ok {
lineStyle = style
}
}
}
if tabs[curTab].CurView == v.Num && !v.Cursor.HasSelection() &&
v.Cursor.Y == char.realLoc.Y && v.Cursor.X == char.realLoc.X && !cursorSet {
screen.ShowCursor(xOffset+char.visualLoc.X, yOffset+char.visualLoc.Y)
cursorSet = true
}
v.SetCursor(&v.Buf.Cursor)
if v.Buf.Settings["cursorline"].(bool) && tabs[curTab].CurView == v.Num &&
!v.Cursor.HasSelection() && v.Cursor.Y == realLineN {
@@ -866,6 +875,16 @@ func (v *View) DisplayView() {
screen.SetContent(xOffset+char.visualLoc.X, yOffset+char.visualLoc.Y, char.drawChar, nil, lineStyle)
for i, c := range v.Buf.cursors {
v.SetCursor(c)
if tabs[curTab].CurView == v.Num && !v.Cursor.HasSelection() &&
v.Cursor.Y == char.realLoc.Y && v.Cursor.X == char.realLoc.X && (!cursorSet || i != 0) {
ShowMultiCursor(xOffset+char.visualLoc.X, yOffset+char.visualLoc.Y, i)
cursorSet = true
}
}
v.SetCursor(&v.Buf.Cursor)
lastChar = char
}
}
@@ -876,19 +895,27 @@ func (v *View) DisplayView() {
var cx, cy int
if lastChar != nil {
lastX = xOffset + lastChar.visualLoc.X + lastChar.width
if tabs[curTab].CurView == v.Num && !v.Cursor.HasSelection() &&
v.Cursor.Y == lastChar.realLoc.Y && v.Cursor.X == lastChar.realLoc.X+1 {
screen.ShowCursor(lastX, yOffset+lastChar.visualLoc.Y)
cx, cy = lastX, yOffset+lastChar.visualLoc.Y
for i, c := range v.Buf.cursors {
v.SetCursor(c)
if tabs[curTab].CurView == v.Num && !v.Cursor.HasSelection() &&
v.Cursor.Y == lastChar.realLoc.Y && v.Cursor.X == lastChar.realLoc.X+1 {
ShowMultiCursor(lastX, yOffset+lastChar.visualLoc.Y, i)
cx, cy = lastX, yOffset+lastChar.visualLoc.Y
}
}
realLoc = Loc{lastChar.realLoc.X, realLineN}
v.SetCursor(&v.Buf.Cursor)
realLoc = Loc{lastChar.realLoc.X + 1, realLineN}
visualLoc = Loc{lastX - xOffset, lastChar.visualLoc.Y}
} else if len(line) == 0 {
if tabs[curTab].CurView == v.Num && !v.Cursor.HasSelection() &&
v.Cursor.Y == realLineN {
screen.ShowCursor(xOffset, yOffset+visualLineN)
cx, cy = xOffset, yOffset+visualLineN
for i, c := range v.Buf.cursors {
v.SetCursor(c)
if tabs[curTab].CurView == v.Num && !v.Cursor.HasSelection() &&
v.Cursor.Y == realLineN {
ShowMultiCursor(xOffset, yOffset+visualLineN, i)
cx, cy = xOffset, yOffset+visualLineN
}
}
v.SetCursor(&v.Buf.Cursor)
lastX = xOffset
realLoc = Loc{0, realLineN}
visualLoc = Loc{0, visualLineN}
@@ -930,6 +957,18 @@ func (v *View) DisplayView() {
}
}
// ShowMultiCursor will display a cursor at a location
// If i == 0 then the terminal cursor will be used
// Otherwise a fake cursor will be drawn at the position
func ShowMultiCursor(x, y, i int) {
if i == 0 {
screen.ShowCursor(x, y)
} else {
r, _, _, _ := screen.GetContent(x, y)
screen.SetContent(x, y, r, nil, defStyle.Reverse(true))
}
}
// Display renders the view, the cursor, and statusline
func (v *View) Display() {
if globalSettings["termtitle"].(bool) {
@@ -937,7 +976,7 @@ func (v *View) Display() {
}
v.DisplayView()
// Don't draw the cursor if it is out of the viewport or if it has a selection
if (v.Cursor.Y-v.Topline < 0 || v.Cursor.Y-v.Topline > v.Height-1) || (v.Cursor.HasSelection() && v.Num == tabs[curTab].CurView) {
if v.Num == tabs[curTab].CurView && (v.Cursor.Y-v.Topline < 0 || v.Cursor.Y-v.Topline > v.Height-1 || v.Cursor.HasSelection()) {
screen.HideCursor()
}
_, screenH := screen.Size()

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<component>
<id>com.github.zyedidia.micro</id>
<name>Micro Text Editor</name>
<summary>A modern and intuitive terminal-based text editor</summary>
<url type="homepage">https://micro-editor.github.io</url>
<url type="bugtracker">https://github.com/zyedidia/micro</url>
<metadata_license>MIT</metadata_license>
<categories>
<category>Development</category>
<category>TextEditor</category>
</categories>
<provides>
<binary>micro</binary>
</provides>
<releases>
<release version="1.2.0" date="2017-05-28" />
</releases>
<developer_name>Zachary Yedidia</developer_name>
<screenshots>
<screenshot type="default">
<caption>Micro Text Editor editing its source code.</caption>
<image type="source">https://raw.githubusercontent.com/zyedidia/micro/master/assets/micro-solarized.png</image>
</screenshot>
</screenshots>
</component>

View File

@@ -0,0 +1,27 @@
color-link default "#CCCCCC,#242424"
color-link comment "#707070,#242424"
color-link identifier "#FFC66D,#242424"
color-link constant "#7A9EC2,#242424"
color-link constant.string "#6A8759,#242424"
color-link constant.string.char "#6A8759,#242424"
color-link statement "#CC8242,#242424"
color-link symbol "#CCCCCC,#242424"
color-link preproc "#CC8242,#242424"
color-link type "#CC8242,#242424"
color-link special "#CC8242,#242424"
color-link underlined "#D33682,#242424"
color-link error "bold #CB4B16,#242424"
color-link todo "bold #D33682,#242424"
color-link statusline "#242424,#CCCCCC"
color-link tabbar "#242424,#CCCCCC"
color-link indent-char "#4F4F4F,#242424"
color-link line-number "#666666,#242424"
color-link current-line-number "#666666,#242424"
color-link gutter-error "#CB4B16,#242424"
color-link gutter-warning "#E6DB74,#242424"
color-link cursor-line "#2C2C2C"
color-link color-column "#2C2C2C"
#No extended types; Plain brackets.
color-link type.extended "default"
#color-link symbol.brackets "default"
color-link symbol.tag "#AE81FF,#242424"

View File

@@ -1,4 +1,3 @@
# This is the monokai colorscheme
color-link default "#F8F8F2,#282828"
color-link comment "#75715E,#282828"
color-link identifier "#66D9EF,#282828"
@@ -14,7 +13,7 @@ color-link underlined "#D33682,#282828"
color-link error "bold #CB4B16,#282828"
color-link todo "bold #D33682,#282828"
color-link statusline "#282828,#F8F8F2"
color-link tabbar "#282828,#f8f8f2"
color-link tabbar "#282828,#F8F8F2"
color-link indent-char "#505050,#282828"
color-link line-number "#AAAAAA,#323232"
color-link current-line-number "#AAAAAA,#282828"
@@ -22,6 +21,7 @@ color-link gutter-error "#CB4B16,#282828"
color-link gutter-warning "#E6DB74,#282828"
color-link cursor-line "#323232"
color-link color-column "#323232"
#No extended types; plain brackets
#No extended types; Plain brackets.
color-link type.extended "default"
color-link symbol.brackets "default"
#color-link symbol.brackets "default"
color-link symbol.tag "#AE81FF,#282828"

View File

@@ -2,22 +2,23 @@ color-link default "#839496,#002833"
color-link comment "#586E75,#002833"
color-link identifier "#268BD2,#002833"
color-link constant "#2AA198,#002833"
color-link constant.specialChar "#DC322F,#002833"
color-link statement "#859900,#002833"
color-link symbol "#859900,#002833"
color-link preproc "#CB4B16,#002833"
color-link type "#B58900,#002833"
color-link special "#DC322F,#002833"
color-link special "#268BD2,#002833"
color-link underlined "#D33682,#002833"
color-link error "bold #CB4B16,#002833"
color-link todo "bold #D33682,#002833"
color-link statusline "#003541,#839496"
color-link tabbar "#003541,#839496"
color-link indent-char "#586E75,#002833"
color-link indent-char "#003541,#002833"
color-link line-number "#586E75,#003541"
color-link current-line-number "#586E75,#002833"
color-link gutter-error "#003541,#CB4B16"
color-link gutter-warning "#CB4B16,#002833"
color-link cursor-line "#003541"
color-link color-column "#003541"
color-link type.extended "default"
color-link symbol.brackets "default"
color-link type.extended "#839496,#002833"
color-link symbol.brackets "#839496,#002833"

View File

@@ -10,12 +10,17 @@ Here are the possible commands that you can use.
* `replace "search" "value" flags`: This will replace `search` with `value`.
The `flags` are optional.
At this point, there is only one flag: `c`, which enables `check` mode
which asks if you'd like to perform the replacement each time.
At this point, there is only one flag: `-a`, which replaces all occurrences
at once.
Note that `search` must be a valid regex. If one of the arguments
does not have any spaces in it, you may omit the quotes.
* `replaceall "search" "value"`: This will replace `search` with `value` without
user confirmation.
See `replace` command for more information.
* `set option value`: sets the option to value. See the `options` help topic
for a list of options you can set.

View File

@@ -1,141 +1,126 @@
#Default Keys
# Default Keys
Below are simple charts of the default hotkeys and their functions.
For more information about binding custom hotkeys or changing
default bindings, please run `>help keybindings`
default bindings, please run `> help keybindings`
Please remember that *all* keys here are rebindable!
If you don't like it, you can change it!
(We are not responsible for you forgetting what you bind keys to.
Do not open an issue because you forgot your keybindings.)
# Power user
#Power user
+--------+---------------------------------------------------------+
| Ctrl+E | Switch to the micro command prompt to run a command. |
| | (See `>help commands` for a list of commands. ) |
+--------+---------------------------------------------------------+
| Ctrl+B | Run shell commands in micro's current working directory.|
+--------+---------------------------------------------------------+
| Key | Description of function |
|-------- |-------------------------------------------------------------------------------------------------- |
| Ctrl+E | Open a command prompt for running commands (see `> help commands` for a list of valid commands). |
| Tab | In command prompt, it will autocomplete if possible. |
| Ctrl+B | Run a shell command (this will close micro while your command executes). |
#Navigation
# Navigation
+--------+---------------------------------------------------------+
| Key | Description of function |
|--------+---------------------------------------------------------+
| Arrows | Move the cursor around your current document. |
| | (Yes this is rebindable to the vim keys if you want.) |
+--------+---------------------------------------------------------+
| Shift+ | Move and select text. |
| Arrows | |
+--------+---------------------------------------------------------+
| Home | Move to the beginning of the current line. (Naturally.) |
+--------+---------------------------------------------------------+
| End | Move to the end of the current line. |
+--------+---------------------------------------------------------+
| PageUp | Move cursor up lines quickly. |
+--------+---------------------------------------------------------+
| PageDn | Move cursor down lines quickly. |
+--------+---------------------------------------------------------+
| Ctrl+L | Jump to line in current file. ( Prompts for line # ) |
+--------+---------------------------------------------------------+
| Ctrl+W | Move between splits open in current tab. |
| | (See vsplit and hsplit in `>help commands`) |
+--------+---------------------------------------------------------+
| Ctrl+T | Open a new tab. |
+--------+---------------------------------------------------------+
| Alt+, | Move to the previous tab in the tablist. |
| | (This works like moving between file buffers in nano) |
+--------+---------------------------------------------------------+
| Alt+. | Move to the next tab in the tablist. |
+--------+---------------------------------------------------------+
| Key | Description of function |
|-------------------------- |------------------------------------------------------------------------------------------ |
| Arrows | Move the cursor around |
| Shift+arrows | Move and select text |
| Home or CtrlLeftArrow | Move to the beginning of the current line |
| End or CtrlRightArrow | Move to the end of the current line |
| AltLeftArrow | Move cursor one word left |
| AltRightArrow | Move cursor one word right |
| PageUp | Move cursor up one page |
| PageDown | Move cursor down one page |
| CtrlHome or CtrlUpArrow | Move cursor to start of document |
| CtrlEnd or CtrlDownArrow | Move cursor to end of document |
| Ctrl+L | Jump to a line in the file (prompts with #) |
| Ctrl+W | Cycle between splits in the current tab (use `> vsplit` or `> hsplit` to create a split) |
#Find Operations
# Tabs
+--------+---------------------------------------------------------+
| Ctrl+F | Find text in current file. ( Prompts for text to find.) |
+--------+---------------------------------------------------------+
| Ctrl+N | Find next instance of current search in current file. |
+--------+---------------------------------------------------------+
| Ctrl+P | Find prev instance of current search in current file. |
+--------+---------------------------------------------------------+
| Key | Description of function |
|-------- |------------------------- |
| Ctrl+T | Open a new tab |
| Alt+, | Previous tab |
| Alt+. | Next tab |
#File Operations
# Find Operations
+--------+---------------------------------------------------------+
| Ctrl+Q | Close current file. ( Quits micro if last file open. ) |
+--------+---------------------------------------------------------+
| Ctrl+O | Open a file. ( Prompts you to input filename. ) |
+--------+---------------------------------------------------------+
| Ctrl+S | Save current file. |
+--------+---------------------------------------------------------+
| Key | Description of function |
|-------- |------------------------------------------ |
| Ctrl+F | Find (opens prompt) |
| Ctrl+N | Find next instance of current search |
| Ctrl+P | Find previous instance of current search |
#Text operations
# File Operations
+--------+---------------------------------------------------------+
| Ctrl+A | Select all text in current file. |
+--------+---------------------------------------------------------+
| Ctrl+X | Cut selected text. |
+--------+---------------------------------------------------------+
| Ctrl+C | Copy selected text. |
+--------+---------------------------------------------------------+
| Ctrl+V | Paste selected text. |
+--------+---------------------------------------------------------+
| Ctrl+K | Cut current line. ( Can then be pasted with Ctrl+V) |
+--------+---------------------------------------------------------+
| Ctrl+D | Duplicate current line. |
+--------+---------------------------------------------------------+
| Ctrl+Z | Undo actions. |
+--------+---------------------------------------------------------+
| Ctrl+Y | Redo actions. |
+--------+---------------------------------------------------------+
| Key | Description of function |
|-------- |---------------------------------------------------------------- |
| Ctrl+Q | Close current file (quits micro if this is the last file open) |
| Ctrl+O | Open a file (prompts for filename) |
| Ctrl+S | Save current file |
#Other
+--------+---------------------------------------------------------+
| Ctrl+G | Open the help file. |
+--------+---------------------------------------------------------+
| Ctrl+H | Alternate backspace. |
| | (Some old terminals don't support the Backspace key .) |
+--------+---------------------------------------------------------+
| Ctrl+R | Toggle the line number ruler. ( On the lefthand side.) |
+--------+---------------------------------------------------------+
# Text operations
#Emacs style actions
| Key | Description of function |
|--------------------------------- |------------------------------------------ |
| AltShiftRightArrow | Select word right |
| AltShiftLeftArrow | Select word left |
| ShiftHome or CtrlShiftLeftArrow | Select to start of current line |
| ShiftEnd or CtrlShiftRightArrow | Select to end of current line |
| CtrlShiftUpArrow | Select to start of file |
| CtrlShiftDownArrow | Select to end of file |
| Ctrl+X | Cut selected text |
| Ctrl+C | Copy selected text |
| Ctrl+V | Paste |
| Ctrl+K | Cut current line |
| Ctrl+D | Duplicate current line |
| Ctrl+Z | Undo |
| Ctrl+Y | Redo |
| AltUpArrow | Move current line or selected lines up |
| AltDownArrow | Move current line of selected lines down |
| AltBackspace or AltCtrl+H | Delete word left |
| Ctrl+A | Select all |
+--------+---------------------------------------------------------+
| Alt+F | Move to the end of the next word. (To the next space.) |
+--------+---------------------------------------------------------+
| Alt+B | Move to the beginning of the previous word. |
+--------+---------------------------------------------------------+
| Alt+A | Alternate Home key. ( Move to beginning of line. ) |
+--------+---------------------------------------------------------+
| Alt+E | Alternate End key. ( Move to the end of line.) |
+--------+---------------------------------------------------------+
| Alt+P | Move cursor up. ( Same as up key. ) |
+--------+---------------------------------------------------------+
| Alt+N | Move cursor down. ( Same as down key. ) |
+--------+---------------------------------------------------------+
# Macros
#Function keys.
| Key | Description of function |
|-------- |---------------------------------------------------------------------------------- |
| Ctrl+U | Toggle macro recording (press Ctrl+U to start recording and press again to stop) |
| Ctrl+J | Run latest recorded macro |
# Multiple cursors
| Key | Description of function |
|---------------- |---------------------------------------------------------------------------------------------- |
| Alt+N | Create new multiple cursor from selection (will select current word if no current selection) |
| Alt+P | Remove latest multiple cursor |
| Alt+C | Remove all multiple cursors (cancel) |
| Alt+X | Skip multiple cursor selection |
| Ctrl-MouseLeft | Place a multiple cursor at any location |
# Other
| Key | Description of function |
|-------- |----------------------------------------------------------------------------------- |
| Ctrl+G | Open help file |
| Ctrl+H | Backspace (old terminals do not support the backspace key and use Ctrl+H instead) |
| Ctrl+R | Toggle the line number ruler |
# Emacs style actions
| Key | Description of function |
|------- |------------------------- |
| Alt+F | Next word |
| Alt+B | Previous word |
| Alt+A | Move to start of line |
| Alt+E | Move to end of line |
# Function keys.
Warning! The function keys may not work in all terminals!
+--------+---------------------------------------------------------+
| F1 | Open help. |
+--------+---------------------------------------------------------+
| F2 | Save current file. |
+--------+---------------------------------------------------------+
| F3 | Find in current file. ( Same as Ctrl+F ) |
+--------+---------------------------------------------------------+
| F4 | Close current file. (Quit if only file.) |
+--------+---------------------------------------------------------+
| F7 | Find in current file. (Same as Ctrl+F) |
+--------+---------------------------------------------------------+
| F10 | Close current file. |
+--------+---------------------------------------------------------+
#Macros
Micro supports the use of keyboard macros. Simply press Ctrl+U to
begin recording a macro and press Ctrl+U to stop recording.
Press Ctrl+J to run your recorded macro.
| Key | Description of function |
|----- |------------------------- |
| F1 | Open help |
| F2 | Save |
| F3 | Find |
| F4 | Quit |
| F7 | Find |
| F10 | Quit |

View File

@@ -15,9 +15,9 @@ and you can see which commands are available by pressing tab, or by
viewing the help topic `> help commands`. When I write `> ...` I mean press
CtrlE and then type whatever is there.
Move the cursor around with the mouse or the arrow keys. Type `>help defaultkeys` to
Move the cursor around with the mouse or the arrow keys. Type `> help defaultkeys` to
get a quick, easy overview of the default hotkeys and what they do. For more info
on rebinding keys, see type `>help keybindings`
on rebinding keys, see type `> help keybindings`
If the colorscheme doesn't look good, you can change it with `> set colorscheme ...`.
You can press tab to see the available colorschemes, or see more information with

View File

@@ -131,9 +131,22 @@ HSplit
PreviousSplit
ToggleMacro
PlayMacro
Suspend (Linux only)
ScrollUp
ScrollDown
SpawnMultiCursor
RemoveMultiCursor
RemoveAllMultiCursors
SkipMultiCursor
UnbindKey
```
You can also bind some mouse actions (these must be bound to mouse buttons)
```
MousePress
MouseMultiCursor
```
Here is the list of all possible keys you can bind:
```
@@ -262,6 +275,18 @@ Escape
Enter
```
You can also bind some mouse buttons (they may be bound to normal actions or mouse actions)
```
MouseLeft
MouseMiddle
MouseRight
MouseWheelUp
MouseWheelDown
MouseWheelLeft
MouseWheelRight
```
# Default keybinding configuration.
```json
@@ -276,10 +301,10 @@ Enter
"ShiftRight": "SelectRight",
"AltLeft": "WordLeft",
"AltRight": "WordRight",
"AltShiftRight": "SelectWordRight",
"AltShiftLeft": "SelectWordLeft",
"AltUp": "MoveLinesUp",
"AltDown": "MoveLinesDown",
"AltShiftRight": "SelectWordRight",
"AltShiftLeft": "SelectWordLeft",
"CtrlLeft": "StartOfLine",
"CtrlRight": "EndOfLine",
"CtrlShiftLeft": "SelectToStartOfLine",
@@ -291,13 +316,12 @@ Enter
"CtrlShiftUp": "SelectToStart",
"CtrlShiftDown": "SelectToEnd",
"Enter": "InsertNewline",
"Space": "InsertSpace",
"CtrlH": "Backspace",
"Backspace": "Backspace",
"Alt-CtrlH": "DeleteWordLeft",
"Alt-Backspace": "DeleteWordLeft",
"Tab": "IndentSelection,InsertTab",
"Backtab": "OutdentSelection",
"Backtab": "OutdentSelection,OutdentLine",
"CtrlO": "OpenFile",
"CtrlS": "Save",
"CtrlF": "Find",
@@ -336,8 +360,6 @@ Enter
"Alt-b": "WordLeft",
"Alt-a": "StartOfLine",
"Alt-e": "EndOfLine",
"Alt-p": "CursorUp",
"Alt-n": "CursorDown",
// Integration with file managers
"F1": "ToggleHelp",
@@ -347,10 +369,23 @@ Enter
"F7": "Find",
"F10": "Quit",
"Esc": "Escape",
// Mouse bindings
"MouseWheelUp": "ScrollUp",
"MouseWheelDown": "ScrollDown",
"MouseLeft": "MousePress",
"MouseMiddle": "PastePrimary",
"Ctrl-MouseLeft": "MouseMultiCursor",
// Multiple cursors bindings
"Alt-n": "SpawnMultiCursor",
"Alt-p": "RemoveMultiCursor",
"Alt-c": "RemoveAllMultiCursors",
"Alt-x": "SkipMultiCursor",
}
```
#Final notes
# Final notes
Note: On some old terminal emulators and on Windows machines, `CtrlH` should be used
for backspace.

View File

@@ -21,6 +21,16 @@ This is almost always the current view, which you can get with `CurView()` as we
All available actions are listed in the keybindings section of the help.
For callbacks to mouse actions, you are also given the event info:
```lua
function onMousePress(view, event)
local x, y = event:Position()
return false
end
```
These functions should also return a boolean specifying whether the view
should be relocated to the cursor or not after the action is complete.
@@ -156,25 +166,25 @@ See this example to learn how to use `MakeCompletion` and `MakeCommand`
```lua
local function StartsWith(String,Start)
String = String:upper()
Start = Start:upper()
return string.sub(String,1,string.len(Start))==Start
String = String:upper()
Start = Start:upper()
return string.sub(String,1,string.len(Start))==Start
end
function complete(input)
local allCompletions = {"Hello", "World", "Foo", "Bar"}
local result = {}
for i,v in pairs(allCompletions) do
if StartsWith(v, input) then
table.insert(result, v)
end
end
return result
local allCompletions = {"Hello", "World", "Foo", "Bar"}
local result = {}
for i,v in pairs(allCompletions) do
if StartsWith(v, input) then
table.insert(result, v)
end
end
return result
end
function foo(arg)
messenger:Message(arg)
messenger:Message(arg)
end
MakeCommand("foo", "example.foo", MakeCompletion("example.complete"))

View File

@@ -1085,6 +1085,7 @@ function onRune(r, v)
-- when converting go structs to lua
-- It needs to be dereferenced because the function expects a non pointer struct
v.Buf:Insert(-v.Cursor.Loc, charAt(autoclosePairs[i], 2))
v:CursorLeft(false)
break
end
end
@@ -1107,6 +1108,7 @@ function preInsertNewline(v)
v:InsertNewline(false)
v:InsertTab(false)
v.Buf:Insert(-v.Cursor.Loc, "\n" .. ws)
v:CursorLeft(false)
return false
end
end

View File

@@ -92,6 +92,10 @@ function split(str, sep)
end
function basename(file)
local name = string.gsub(file, "(.*/)(.*)", "%2")
local sep = "/"
if OS == "windows" then
sep = "\\"
end
local name = string.gsub(file, "(.*" .. sep .. ")(.*)", "%2")
return name
end

View File

@@ -10,7 +10,7 @@ rules:
- type.extended: "\\b(bool)\\b"
- statement: "\\b(typename|mutable|volatile|register|explicit)\\b"
- statement: "\\b(for|if|while|do|else|case|default|switch)\\b"
- statement: "\\b(try|throw|catch|operator|new|delete)\\b"
- statement: "\\b(try|throw|catch|operator|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\\\\]))'"

View File

@@ -0,0 +1,36 @@
filetype: crontab
detect:
filename: "crontab$"
header: "^#.*?/etc/crontab"
rules:
# The time and date fields are:
# field allowed values
# ----- --------------
# minute 0-59
# hour 0-23
# day of month 0-31
# month 0-12 (or names, see below)
# day of week 0-7 (0 or 7 is Sun, or use names)
- statement: "^([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)\\s+(.*)$\\n?"
- constant: "^([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)\\s+([\\*0-9,\\-\\/]+)"
# Shell Values
- type: "^[A-Z]+\\="
# Months and weekday keywords
- type: "jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec"
- constant: "sun|mon|tue|wed|thu|fri|sat"
- type: "\\@(reboot|yearly|annually|monthly|weekly|daily|midnight|hourly)"
# Conditionals
- special: "(\\{|\\}|\\(|\\)|\\;|\\]|\\[|`|\\\\|\\$|<|>|^|!|=|&|\\|)"
- comment:
start: "#"
end: "$"
rules:
- todo: "(TODO|XXX|FIXME):?"

55
runtime/syntax/twig.yaml Normal file
View File

@@ -0,0 +1,55 @@
filetype: twig
detect:
filename: "\\.twig$"
rules:
- include: "html"
- symbol.tag:
start: "\\{\\{[[:space:]]"
end: "[[:space:]]\\}\\}"
rules:
- identifier: "\\b(abs|batch|capitalize|convert|encoding|date(_modify)?|default|escape|first|format|join|json_encode|keys|last|length|lower|merge|nl2br|number_format|raw|replace|reverse|round|slice|sort|split|striptags|title|trim|upper|url_encode)\\b"
- identifier.class: "\\b(attribute|block|constant|cycle|date|dump|include|max|min|parent|random|range|source|template_from_string)\\b"
- type.keyword: "\\b(and|as|constant|defined|divisibleby|empty|even|false|in|is|iterable|not|null|odd|or|same(as)?|true|with)\\b"
- symbol.operator: "[.:;,+*?|=!\\%]|<|>|/|-|&"
- symbol.brackets: "[(){}]|\\[|\\]"
- constant.number: "\\b[0-9]+\\b|\\b0x[0-9A-Fa-f]+\\b"
- constant.string:
start: "\""
end: "\""
skip: "\\\\"
rules:
- constant.specialChar: "\\\\."
- constant.string:
start: "'"
end: "'"
skip: "\\\\"
rules:
- constant.specialChar: "\\\\."
- symbol.tag:
start: "\\{%[[:space:]]"
end: "[[:space:]]%\\}"
rules:
- identifier: "\\b(abs|batch|capitalize|convert|encoding|date(_modify)?|default|escape|first|format|join|json_encode|keys|last|length|lower|merge|nl2br|number_format|raw|replace|reverse|round|slice|sort|split|striptags|title|trim|upper|url_encode)\\b"
- identifier.class: "\\b(attribute|block|constant|cycle|date|dump|include|max|min|parent|random|range|source|template_from_string)\\b"
- type.keyword: "\\b(and|as|constant|defined|divisibleby|empty|even|false|in|is|iterable|not|null|odd|or|same(as)?|true|with)\\b"
- symbol.operator: "[.:;,+*?|=!\\%]|<|>|/|-|&"
- symbol.brackets: "[(){}]|\\[|\\]"
- constant.number: "\\b[0-9]+\\b|\\b0x[0-9A-Fa-f]+\\b"
- constant.string:
start: "\""
end: "\""
skip: "\\\\"
rules:
- constant.specialChar: "\\\\."
- constant.string:
start: "'"
end: "'"
skip: "\\\\"
rules:
- constant.specialChar: "\\\\."
- comment:
start: "\\{#"
end: "#\\}"
rules: []

View File

@@ -4,7 +4,7 @@ detect:
filename: "\\.vala$"
rules:
- type: "\\b(float|double|bool|char|int|uint|short|long|void|(un)?signed)\\b"
- type: "\\b(float|double|bool|u?char|u?int(8|16|32|64)?|u?short|u?long|void|s?size_t|unichar)\\b"
- identifier.class: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[()]"
- statement: "\\b(for|if|while|do|else|case|default|switch|try|throw|catch)\\b"
- statement: "\\b(inline|typedef|struct|enum|union|extern|static|const)\\b"

View File

@@ -9,6 +9,6 @@ rules:
- constant.number: "\\b[0-9]+\\b"
- symbol.operator: "[*:=]"
- constant.bool: "\\b(true|false)\\b"
- comment: "(^|[[:space:]])#([^{].*)?$"
- comment: "(^|[[:space:]])!([^{].*)?$"
- indent-char.whitespace: "[[:space:]]+$"
- indent-char: " + +| + +"

View File

@@ -14,5 +14,17 @@ apps:
parts:
micro:
source: .
plugin: go
go-importpath: github.com/zyedidia/micro/cmd/micro
source-type: git
plugin: nil
build-packages: [golang-go, make]
prepare: |
mkdir -p ../go/src/github.com/zyedidia/micro
cp -R . ../go/src/github.com/zyedidia/micro
build: |
export GOPATH=$(pwd)/../go
export GOBIN=$(pwd)/../go/bin
cd ../go/src/github.com/zyedidia/micro
make install
install: |
mkdir $SNAPCRAFT_PART_INSTALL/bin
mv ../go/bin/micro $SNAPCRAFT_PART_INSTALL/bin/

View File

@@ -5,7 +5,7 @@ import (
"os/exec"
"strings"
"github.com/blang/semver"
"github.com/zyedidia/micro/tools/semver"
)
func getTag(match ...string) (string, *semver.PRVersion) {

View File

@@ -1,5 +1,4 @@
# This script creates the nightly release on Github for micro
# It assumes that the binaries are in the current directory
# You must have the correct Github access token to run this script
echo "Deleting old release"
@@ -23,6 +22,10 @@ github-release release \
--description "Autogenerated nightly build of micro" \
--pre-release
echo "Cross compiling binaries"
./cross-compile.sh $1
mv ../binaries .
echo "Uploading OSX binary"
github-release upload \
--user zyedidia \

View File

@@ -1,5 +1,4 @@
# This script creates releases on Github for micro
# It assumes that the binaries are in the current directory
# You must have the correct Github access token to run this script
# $1 is the title, $2 is the description
@@ -20,6 +19,10 @@ github-release release \
--description "$2" \
--pre-release
echo "Cross compiling binaries"
./cross-compile.sh $1
mv ../binaries .
echo "Uploading OSX binary"
github-release upload \
--user zyedidia \

View File

@@ -1,5 +1,4 @@
# This script creates releases on Github for micro
# It assumes that the binaries are in the current directory
# You must have the correct Github access token to run this script
# $1 is the title, $2 is the description
@@ -19,6 +18,10 @@ github-release release \
--name "$1" \
--description "$2" \
echo "Cross compiling binaries"
./cross-compile.sh $1
mv ../binaries .
echo "Uploading OSX binary"
github-release upload \
--user zyedidia \

23
tools/semver/json.go Normal file
View File

@@ -0,0 +1,23 @@
package semver
import (
"encoding/json"
)
// MarshalJSON implements the encoding/json.Marshaler interface.
func (v Version) MarshalJSON() ([]byte, error) {
return json.Marshal(v.String())
}
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
func (v *Version) UnmarshalJSON(data []byte) (err error) {
var versionString string
if err = json.Unmarshal(data, &versionString); err != nil {
return
}
*v, err = Parse(versionString)
return
}

416
tools/semver/range.go Normal file
View File

@@ -0,0 +1,416 @@
package semver
import (
"fmt"
"strconv"
"strings"
"unicode"
)
type wildcardType int
const (
noneWildcard wildcardType = iota
majorWildcard wildcardType = 1
minorWildcard wildcardType = 2
patchWildcard wildcardType = 3
)
func wildcardTypefromInt(i int) wildcardType {
switch i {
case 1:
return majorWildcard
case 2:
return minorWildcard
case 3:
return patchWildcard
default:
return noneWildcard
}
}
type comparator func(Version, Version) bool
var (
compEQ comparator = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) == 0
}
compNE = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) != 0
}
compGT = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) == 1
}
compGE = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) >= 0
}
compLT = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) == -1
}
compLE = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) <= 0
}
)
type versionRange struct {
v Version
c comparator
}
// rangeFunc creates a Range from the given versionRange.
func (vr *versionRange) rangeFunc() Range {
return Range(func(v Version) bool {
return vr.c(v, vr.v)
})
}
// Range represents a range of versions.
// A Range can be used to check if a Version satisfies it:
//
// range, err := semver.ParseRange(">1.0.0 <2.0.0")
// range(semver.MustParse("1.1.1") // returns true
type Range func(Version) bool
// OR combines the existing Range with another Range using logical OR.
func (rf Range) OR(f Range) Range {
return Range(func(v Version) bool {
return rf(v) || f(v)
})
}
// AND combines the existing Range with another Range using logical AND.
func (rf Range) AND(f Range) Range {
return Range(func(v Version) bool {
return rf(v) && f(v)
})
}
// ParseRange parses a range and returns a Range.
// If the range could not be parsed an error is returned.
//
// Valid ranges are:
// - "<1.0.0"
// - "<=1.0.0"
// - ">1.0.0"
// - ">=1.0.0"
// - "1.0.0", "=1.0.0", "==1.0.0"
// - "!1.0.0", "!=1.0.0"
//
// A Range can consist of multiple ranges separated by space:
// Ranges can be linked by logical AND:
// - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0"
// - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2
//
// Ranges can also be linked by logical OR:
// - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"
//
// AND has a higher precedence than OR. It's not possible to use brackets.
//
// Ranges can be combined by both AND and OR
//
// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
func ParseRange(s string) (Range, error) {
parts := splitAndTrim(s)
orParts, err := splitORParts(parts)
if err != nil {
return nil, err
}
expandedParts, err := expandWildcardVersion(orParts)
if err != nil {
return nil, err
}
var orFn Range
for _, p := range expandedParts {
var andFn Range
for _, ap := range p {
opStr, vStr, err := splitComparatorVersion(ap)
if err != nil {
return nil, err
}
vr, err := buildVersionRange(opStr, vStr)
if err != nil {
return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err)
}
rf := vr.rangeFunc()
// Set function
if andFn == nil {
andFn = rf
} else { // Combine with existing function
andFn = andFn.AND(rf)
}
}
if orFn == nil {
orFn = andFn
} else {
orFn = orFn.OR(andFn)
}
}
return orFn, nil
}
// splitORParts splits the already cleaned parts by '||'.
// Checks for invalid positions of the operator and returns an
// error if found.
func splitORParts(parts []string) ([][]string, error) {
var ORparts [][]string
last := 0
for i, p := range parts {
if p == "||" {
if i == 0 {
return nil, fmt.Errorf("First element in range is '||'")
}
ORparts = append(ORparts, parts[last:i])
last = i + 1
}
}
if last == len(parts) {
return nil, fmt.Errorf("Last element in range is '||'")
}
ORparts = append(ORparts, parts[last:])
return ORparts, nil
}
// buildVersionRange takes a slice of 2: operator and version
// and builds a versionRange, otherwise an error.
func buildVersionRange(opStr, vStr string) (*versionRange, error) {
c := parseComparator(opStr)
if c == nil {
return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, ""))
}
v, err := Parse(vStr)
if err != nil {
return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err)
}
return &versionRange{
v: v,
c: c,
}, nil
}
// inArray checks if a byte is contained in an array of bytes
func inArray(s byte, list []byte) bool {
for _, el := range list {
if el == s {
return true
}
}
return false
}
// splitAndTrim splits a range string by spaces and cleans whitespaces
func splitAndTrim(s string) (result []string) {
last := 0
var lastChar byte
excludeFromSplit := []byte{'>', '<', '='}
for i := 0; i < len(s); i++ {
if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) {
if last < i-1 {
result = append(result, s[last:i])
}
last = i + 1
} else if s[i] != ' ' {
lastChar = s[i]
}
}
if last < len(s)-1 {
result = append(result, s[last:])
}
for i, v := range result {
result[i] = strings.Replace(v, " ", "", -1)
}
// parts := strings.Split(s, " ")
// for _, x := range parts {
// if s := strings.TrimSpace(x); len(s) != 0 {
// result = append(result, s)
// }
// }
return
}
// splitComparatorVersion splits the comparator from the version.
// Input must be free of leading or trailing spaces.
func splitComparatorVersion(s string) (string, string, error) {
i := strings.IndexFunc(s, unicode.IsDigit)
if i == -1 {
return "", "", fmt.Errorf("Could not get version from string: %q", s)
}
return strings.TrimSpace(s[0:i]), s[i:], nil
}
// getWildcardType will return the type of wildcard that the
// passed version contains
func getWildcardType(vStr string) wildcardType {
parts := strings.Split(vStr, ".")
nparts := len(parts)
wildcard := parts[nparts-1]
possibleWildcardType := wildcardTypefromInt(nparts)
if wildcard == "x" {
return possibleWildcardType
}
return noneWildcard
}
// createVersionFromWildcard will convert a wildcard version
// into a regular version, replacing 'x's with '0's, handling
// special cases like '1.x.x' and '1.x'
func createVersionFromWildcard(vStr string) string {
// handle 1.x.x
vStr2 := strings.Replace(vStr, ".x.x", ".x", 1)
vStr2 = strings.Replace(vStr2, ".x", ".0", 1)
parts := strings.Split(vStr2, ".")
// handle 1.x
if len(parts) == 2 {
return vStr2 + ".0"
}
return vStr2
}
// incrementMajorVersion will increment the major version
// of the passed version
func incrementMajorVersion(vStr string) (string, error) {
parts := strings.Split(vStr, ".")
i, err := strconv.Atoi(parts[0])
if err != nil {
return "", err
}
parts[0] = strconv.Itoa(i + 1)
return strings.Join(parts, "."), nil
}
// incrementMajorVersion will increment the minor version
// of the passed version
func incrementMinorVersion(vStr string) (string, error) {
parts := strings.Split(vStr, ".")
i, err := strconv.Atoi(parts[1])
if err != nil {
return "", err
}
parts[1] = strconv.Itoa(i + 1)
return strings.Join(parts, "."), nil
}
// expandWildcardVersion will expand wildcards inside versions
// following these rules:
//
// * when dealing with patch wildcards:
// >= 1.2.x will become >= 1.2.0
// <= 1.2.x will become < 1.3.0
// > 1.2.x will become >= 1.3.0
// < 1.2.x will become < 1.2.0
// != 1.2.x will become < 1.2.0 >= 1.3.0
//
// * when dealing with minor wildcards:
// >= 1.x will become >= 1.0.0
// <= 1.x will become < 2.0.0
// > 1.x will become >= 2.0.0
// < 1.0 will become < 1.0.0
// != 1.x will become < 1.0.0 >= 2.0.0
//
// * when dealing with wildcards without
// version operator:
// 1.2.x will become >= 1.2.0 < 1.3.0
// 1.x will become >= 1.0.0 < 2.0.0
func expandWildcardVersion(parts [][]string) ([][]string, error) {
var expandedParts [][]string
for _, p := range parts {
var newParts []string
for _, ap := range p {
if strings.Index(ap, "x") != -1 {
opStr, vStr, err := splitComparatorVersion(ap)
if err != nil {
return nil, err
}
versionWildcardType := getWildcardType(vStr)
flatVersion := createVersionFromWildcard(vStr)
var resultOperator string
var shouldIncrementVersion bool
switch opStr {
case ">":
resultOperator = ">="
shouldIncrementVersion = true
case ">=":
resultOperator = ">="
case "<":
resultOperator = "<"
case "<=":
resultOperator = "<"
shouldIncrementVersion = true
case "", "=", "==":
newParts = append(newParts, ">="+flatVersion)
resultOperator = "<"
shouldIncrementVersion = true
case "!=", "!":
newParts = append(newParts, "<"+flatVersion)
resultOperator = ">="
shouldIncrementVersion = true
}
var resultVersion string
if shouldIncrementVersion {
switch versionWildcardType {
case patchWildcard:
resultVersion, _ = incrementMinorVersion(flatVersion)
case minorWildcard:
resultVersion, _ = incrementMajorVersion(flatVersion)
}
} else {
resultVersion = flatVersion
}
ap = resultOperator + resultVersion
}
newParts = append(newParts, ap)
}
expandedParts = append(expandedParts, newParts)
}
return expandedParts, nil
}
func parseComparator(s string) comparator {
switch s {
case "==":
fallthrough
case "":
fallthrough
case "=":
return compEQ
case ">":
return compGT
case ">=":
return compGE
case "<":
return compLT
case "<=":
return compLE
case "!":
fallthrough
case "!=":
return compNE
}
return nil
}
// MustParseRange is like ParseRange but panics if the range cannot be parsed.
func MustParseRange(s string) Range {
r, err := ParseRange(s)
if err != nil {
panic(`semver: ParseRange(` + s + `): ` + err.Error())
}
return r
}

418
tools/semver/semver.go Normal file
View File

@@ -0,0 +1,418 @@
package semver
import (
"errors"
"fmt"
"strconv"
"strings"
)
const (
numbers string = "0123456789"
alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"
alphanum = alphas + numbers
)
// SpecVersion is the latest fully supported spec version of semver
var SpecVersion = Version{
Major: 2,
Minor: 0,
Patch: 0,
}
// Version represents a semver compatible version
type Version struct {
Major uint64
Minor uint64
Patch uint64
Pre []PRVersion
Build []string //No Precendence
}
// Version to string
func (v Version) String() string {
b := make([]byte, 0, 5)
b = strconv.AppendUint(b, v.Major, 10)
b = append(b, '.')
b = strconv.AppendUint(b, v.Minor, 10)
b = append(b, '.')
b = strconv.AppendUint(b, v.Patch, 10)
if len(v.Pre) > 0 {
b = append(b, '-')
b = append(b, v.Pre[0].String()...)
for _, pre := range v.Pre[1:] {
b = append(b, '.')
b = append(b, pre.String()...)
}
}
if len(v.Build) > 0 {
b = append(b, '+')
b = append(b, v.Build[0]...)
for _, build := range v.Build[1:] {
b = append(b, '.')
b = append(b, build...)
}
}
return string(b)
}
// Equals checks if v is equal to o.
func (v Version) Equals(o Version) bool {
return (v.Compare(o) == 0)
}
// EQ checks if v is equal to o.
func (v Version) EQ(o Version) bool {
return (v.Compare(o) == 0)
}
// NE checks if v is not equal to o.
func (v Version) NE(o Version) bool {
return (v.Compare(o) != 0)
}
// GT checks if v is greater than o.
func (v Version) GT(o Version) bool {
return (v.Compare(o) == 1)
}
// GTE checks if v is greater than or equal to o.
func (v Version) GTE(o Version) bool {
return (v.Compare(o) >= 0)
}
// GE checks if v is greater than or equal to o.
func (v Version) GE(o Version) bool {
return (v.Compare(o) >= 0)
}
// LT checks if v is less than o.
func (v Version) LT(o Version) bool {
return (v.Compare(o) == -1)
}
// LTE checks if v is less than or equal to o.
func (v Version) LTE(o Version) bool {
return (v.Compare(o) <= 0)
}
// LE checks if v is less than or equal to o.
func (v Version) LE(o Version) bool {
return (v.Compare(o) <= 0)
}
// Compare compares Versions v to o:
// -1 == v is less than o
// 0 == v is equal to o
// 1 == v is greater than o
func (v Version) Compare(o Version) int {
if v.Major != o.Major {
if v.Major > o.Major {
return 1
}
return -1
}
if v.Minor != o.Minor {
if v.Minor > o.Minor {
return 1
}
return -1
}
if v.Patch != o.Patch {
if v.Patch > o.Patch {
return 1
}
return -1
}
// Quick comparison if a version has no prerelease versions
if len(v.Pre) == 0 && len(o.Pre) == 0 {
return 0
} else if len(v.Pre) == 0 && len(o.Pre) > 0 {
return 1
} else if len(v.Pre) > 0 && len(o.Pre) == 0 {
return -1
}
i := 0
for ; i < len(v.Pre) && i < len(o.Pre); i++ {
if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 {
continue
} else if comp == 1 {
return 1
} else {
return -1
}
}
// If all pr versions are the equal but one has further prversion, this one greater
if i == len(v.Pre) && i == len(o.Pre) {
return 0
} else if i == len(v.Pre) && i < len(o.Pre) {
return -1
} else {
return 1
}
}
// Validate validates v and returns error in case
func (v Version) Validate() error {
// Major, Minor, Patch already validated using uint64
for _, pre := range v.Pre {
if !pre.IsNum { //Numeric prerelease versions already uint64
if len(pre.VersionStr) == 0 {
return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr)
}
if !containsOnly(pre.VersionStr, alphanum) {
return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr)
}
}
}
for _, build := range v.Build {
if len(build) == 0 {
return fmt.Errorf("Build meta data can not be empty %q", build)
}
if !containsOnly(build, alphanum) {
return fmt.Errorf("Invalid character(s) found in build meta data %q", build)
}
}
return nil
}
// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error
func New(s string) (vp *Version, err error) {
v, err := Parse(s)
vp = &v
return
}
// Make is an alias for Parse, parses version string and returns a validated Version or error
func Make(s string) (Version, error) {
return Parse(s)
}
// ParseTolerant allows for certain version specifications that do not strictly adhere to semver
// specs to be parsed by this library. It does so by normalizing versions before passing them to
// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions
// with only major and minor components specified
func ParseTolerant(s string) (Version, error) {
s = strings.TrimSpace(s)
s = strings.TrimPrefix(s, "v")
// Split into major.minor.(patch+pr+meta)
parts := strings.SplitN(s, ".", 3)
if len(parts) < 3 {
if strings.ContainsAny(parts[len(parts)-1], "+-") {
return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data")
}
for len(parts) < 3 {
parts = append(parts, "0")
}
s = strings.Join(parts, ".")
}
return Parse(s)
}
// Parse parses version string and returns a validated Version or error
func Parse(s string) (Version, error) {
if len(s) == 0 {
return Version{}, errors.New("Version string empty")
}
// Split into major.minor.(patch+pr+meta)
parts := strings.SplitN(s, ".", 3)
if len(parts) != 3 {
return Version{}, errors.New("No Major.Minor.Patch elements found")
}
// Major
if !containsOnly(parts[0], numbers) {
return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0])
}
if hasLeadingZeroes(parts[0]) {
return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0])
}
major, err := strconv.ParseUint(parts[0], 10, 64)
if err != nil {
return Version{}, err
}
// Minor
if !containsOnly(parts[1], numbers) {
return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1])
}
if hasLeadingZeroes(parts[1]) {
return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1])
}
minor, err := strconv.ParseUint(parts[1], 10, 64)
if err != nil {
return Version{}, err
}
v := Version{}
v.Major = major
v.Minor = minor
var build, prerelease []string
patchStr := parts[2]
if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 {
build = strings.Split(patchStr[buildIndex+1:], ".")
patchStr = patchStr[:buildIndex]
}
if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 {
prerelease = strings.Split(patchStr[preIndex+1:], ".")
patchStr = patchStr[:preIndex]
}
if !containsOnly(patchStr, numbers) {
return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr)
}
if hasLeadingZeroes(patchStr) {
return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr)
}
patch, err := strconv.ParseUint(patchStr, 10, 64)
if err != nil {
return Version{}, err
}
v.Patch = patch
// Prerelease
for _, prstr := range prerelease {
parsedPR, err := NewPRVersion(prstr)
if err != nil {
return Version{}, err
}
v.Pre = append(v.Pre, parsedPR)
}
// Build meta data
for _, str := range build {
if len(str) == 0 {
return Version{}, errors.New("Build meta data is empty")
}
if !containsOnly(str, alphanum) {
return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str)
}
v.Build = append(v.Build, str)
}
return v, nil
}
// MustParse is like Parse but panics if the version cannot be parsed.
func MustParse(s string) Version {
v, err := Parse(s)
if err != nil {
panic(`semver: Parse(` + s + `): ` + err.Error())
}
return v
}
// PRVersion represents a PreRelease Version
type PRVersion struct {
VersionStr string
VersionNum uint64
IsNum bool
}
// NewPRVersion creates a new valid prerelease version
func NewPRVersion(s string) (PRVersion, error) {
if len(s) == 0 {
return PRVersion{}, errors.New("Prerelease is empty")
}
v := PRVersion{}
if containsOnly(s, numbers) {
if hasLeadingZeroes(s) {
return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s)
}
num, err := strconv.ParseUint(s, 10, 64)
// Might never be hit, but just in case
if err != nil {
return PRVersion{}, err
}
v.VersionNum = num
v.IsNum = true
} else if containsOnly(s, alphanum) {
v.VersionStr = s
v.IsNum = false
} else {
return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s)
}
return v, nil
}
// IsNumeric checks if prerelease-version is numeric
func (v PRVersion) IsNumeric() bool {
return v.IsNum
}
// Compare compares two PreRelease Versions v and o:
// -1 == v is less than o
// 0 == v is equal to o
// 1 == v is greater than o
func (v PRVersion) Compare(o PRVersion) int {
if v.IsNum && !o.IsNum {
return -1
} else if !v.IsNum && o.IsNum {
return 1
} else if v.IsNum && o.IsNum {
if v.VersionNum == o.VersionNum {
return 0
} else if v.VersionNum > o.VersionNum {
return 1
} else {
return -1
}
} else { // both are Alphas
if v.VersionStr == o.VersionStr {
return 0
} else if v.VersionStr > o.VersionStr {
return 1
} else {
return -1
}
}
}
// PreRelease version to string
func (v PRVersion) String() string {
if v.IsNum {
return strconv.FormatUint(v.VersionNum, 10)
}
return v.VersionStr
}
func containsOnly(s string, set string) bool {
return strings.IndexFunc(s, func(r rune) bool {
return !strings.ContainsRune(set, r)
}) == -1
}
func hasLeadingZeroes(s string) bool {
return len(s) > 1 && s[0] == '0'
}
// NewBuildVersion creates a new valid build version
func NewBuildVersion(s string) (string, error) {
if len(s) == 0 {
return "", errors.New("Buildversion is empty")
}
if !containsOnly(s, alphanum) {
return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s)
}
return s, nil
}

28
tools/semver/sort.go Normal file
View File

@@ -0,0 +1,28 @@
package semver
import (
"sort"
)
// Versions represents multiple versions.
type Versions []Version
// Len returns length of version collection
func (s Versions) Len() int {
return len(s)
}
// Swap swaps two versions inside the collection by its indices
func (s Versions) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less checks if version at index i is less than version at index j
func (s Versions) Less(i, j int) bool {
return s[i].LT(s[j])
}
// Sort sorts a slice of versions
func Sort(versions []Version) {
sort.Sort(Versions(versions))
}

30
tools/semver/sql.go Normal file
View File

@@ -0,0 +1,30 @@
package semver
import (
"database/sql/driver"
"fmt"
)
// Scan implements the database/sql.Scanner interface.
func (v *Version) Scan(src interface{}) (err error) {
var str string
switch src := src.(type) {
case string:
str = src
case []byte:
str = string(src)
default:
return fmt.Errorf("Version.Scan: cannot convert %T to string.", src)
}
if t, err := Parse(str); err == nil {
*v = t
}
return
}
// Value implements the database/sql/driver.Valuer interface.
func (v Version) Value() (driver.Value, error) {
return v.String(), nil
}

View File

@@ -1,14 +1,6 @@
cd ../cmd/micro
govendor init
govendor add +e
cd ../../..
cd ../..
tar czf "$1".tar.gz micro
zip -rq "$1".zip micro
mv "$1".tar.gz micro
mv "$1".zip micro
cd micro/cmd/micro
rm -rf vendor