mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-29 22:27:13 +09:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd8776cdd0 |
@@ -1,7 +0,0 @@
|
||||
# See http://editorconfig.org
|
||||
|
||||
# In Go files we indent with tabs but still
|
||||
# set indent_size to control the GitHub web viewer.
|
||||
[*.go]
|
||||
indent_size=4
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,5 +1,3 @@
|
||||
.DS_Store
|
||||
|
||||
micro
|
||||
!cmd/micro
|
||||
binaries/
|
||||
@@ -7,3 +5,4 @@ tmp.sh
|
||||
test/
|
||||
.idea/
|
||||
packages/
|
||||
cmd/micro/vendor
|
||||
|
||||
63
.gitmodules
vendored
63
.gitmodules
vendored
@@ -1,63 +0,0 @@
|
||||
[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/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
|
||||
[submodule "cmd/micro/vendor/github.com/flynn/json5"]
|
||||
path = cmd/micro/vendor/github.com/flynn/json5
|
||||
url = https://github.com/flynn/json5
|
||||
[submodule "cmd/micro/vendor/github.com/zyedidia/terminal"]
|
||||
path = cmd/micro/vendor/github.com/zyedidia/terminal
|
||||
url = https://github.com/zyedidia/terminal
|
||||
[submodule "cmd/micro/vendor/github.com/zyedidia/pty"]
|
||||
path = cmd/micro/vendor/github.com/zyedidia/pty
|
||||
url = https://github.com/zyedidia/pty
|
||||
4
LICENSE
4
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
Micro is licensed under the MIT "Expat" License:
|
||||
|
||||
Copyright (c) 2016-2017: Zachary Yedidia, et al.
|
||||
Copyright (c) 2016: Zachary Yedidia, et al.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1106,111 +1106,3 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
github.com/flynn/json5/LICENSE
|
||||
================
|
||||
|
||||
Decoder code based on package encoding/json from the Go language.
|
||||
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
|
||||
Test data based on the parse cases from https://github.com/json5/json5
|
||||
|
||||
Copyright (c) 2012-2016 Aseem Kishore, and others.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
github.com/james4k/terminal/LICENSE
|
||||
================
|
||||
|
||||
Copyright (C) 2013 James Gray
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without liitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and thismssion notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
github.com/kr/pty/License
|
||||
================
|
||||
|
||||
Copyright (c) 2011 Keith Rarick
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall
|
||||
be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||||
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
11
Makefile
11
Makefile
@@ -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: update
|
||||
build: deps
|
||||
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: update
|
||||
install: deps
|
||||
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,9 +32,13 @@ 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
|
||||
git submodule update --init
|
||||
go get -u -d ./cmd/micro
|
||||
|
||||
# Builds the runtime
|
||||
runtime:
|
||||
@@ -43,6 +47,7 @@ runtime:
|
||||
mv runtime.go cmd/micro
|
||||
|
||||
test:
|
||||
go get -d ./cmd/micro
|
||||
go test ./cmd/micro
|
||||
|
||||
clean:
|
||||
|
||||
65
README.md
65
README.md
@@ -4,7 +4,6 @@
|
||||
[](https://goreportcard.com/report/github.com/zyedidia/micro)
|
||||
[](https://gitter.im/zyedidia/micro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://github.com/zyedidia/micro/blob/master/LICENSE)
|
||||
[](https://build.snapcraft.io/user/zyedidia/micro)
|
||||
|
||||
Micro is a terminal-based text editor that aims to be easy to use and intuitive, while also taking advantage of the full capabilities
|
||||
of modern terminals. It comes as one single, batteries-included, static binary with no dependencies, and you can download and use it right now.
|
||||
@@ -20,33 +19,15 @@ To see more screenshots of micro, showcasing all of the default colorschemes, se
|
||||
|
||||
You can also check out the website for Micro at https://micro-editor.github.io.
|
||||
|
||||
# Table of Contents
|
||||
- [Features](#features)
|
||||
- [Installation](#installation)
|
||||
- [Prebuilt binaries](#prebuilt-binaries)
|
||||
- [Package Managers](#package-managers)
|
||||
- [Building from source](#building-from-source)
|
||||
- [MacOS terminal](#macos-terminal)
|
||||
- [Linux clipboard support](#linux-clipboard-support)
|
||||
- [Colors and syntax highlighting](#colors-and-syntax-highlighting)
|
||||
- [Plan9, Cygwin](#plan9-cygwin)
|
||||
- [Usage](#usage)
|
||||
- [Documentation and Help](#documentation-and-help)
|
||||
- [Contributing](#contributing)
|
||||
|
||||
- - -
|
||||
|
||||
# Features
|
||||
|
||||
* 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
|
||||
* You shouldn't have to configure much out of the box (and it is extremely easy to configure)
|
||||
* Splits and tabs
|
||||
* Nano-like menu to help you remember the keybindings
|
||||
* Extremely good mouse support
|
||||
* This means mouse dragging to create a selection, double click to select by word, and triple click to select by line
|
||||
* Cross platform (It should work on all the platforms Go runs on)
|
||||
@@ -67,7 +48,7 @@ You can also check out the website for Micro at https://micro-editor.github.io.
|
||||
* Macros
|
||||
* Common editor things such as undo/redo, line numbers, Unicode support, softwrap...
|
||||
|
||||
Although not yet implemented, I hope to add more features such as autocompletion ([#174](https://github.com/zyedidia/micro/issues/174)) or a tree view ([#249](https://github.com/zyedidia/micro/issues/249)) in the future.
|
||||
Although not yet implemented, I hope to add more features such as autocompletion ([#174](https://github.com/zyedidia/micro/issues/174)), and multiple cursors ([#5](https://github.com/zyedidia/micro/issues/5)) in the future.
|
||||
|
||||
# Installation
|
||||
|
||||
@@ -86,19 +67,7 @@ and you'll see all the stable releases with the corresponding binaries.
|
||||
|
||||
If you'd like to see more information after installing micro, run `micro -version`.
|
||||
|
||||
### Installation script
|
||||
|
||||
There is a great script which can install micro for you by downloading the latest prebuilt binary. You can find it at https://getmic.ro (the github repo for it is [here](https://github.com/benweissmann/getmic.ro)).
|
||||
|
||||
Then you can easily install micro:
|
||||
|
||||
$ curl https://getmic.ro | bash
|
||||
|
||||
The script will install the micro binary to the current directory.
|
||||
|
||||
See the [Github page](https://github.com/benweissmann/getmic.ro) for more information.
|
||||
|
||||
### Package managers
|
||||
### Package Managers
|
||||
|
||||
You can install micro using Homebrew on Mac:
|
||||
|
||||
@@ -106,51 +75,39 @@ You can install micro using Homebrew on Mac:
|
||||
brew install micro
|
||||
```
|
||||
|
||||
On Windows, you can install micro through [Chocolatey](https://chocolatey.org/) or [Scoop](https://github.com/lukesampson/scoop):
|
||||
On Windows, you can install micro through Chocolatey:
|
||||
|
||||
```
|
||||
choco install micro
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
scoop install micro
|
||||
```
|
||||
|
||||
On Linux, you can install micro through [snap](https://snapcraft.io/docs/core/install)
|
||||
|
||||
```
|
||||
snap install micro --classic
|
||||
```
|
||||
|
||||
On OpenBSD, micro is available in the ports tree. It is also available as a binary package.
|
||||
|
||||
```
|
||||
pkg_add -v micro
|
||||
snap install micro --beta
|
||||
```
|
||||
|
||||
### Building from source
|
||||
|
||||
If your operating system does not have a binary release, but does run Go, you can build from source.
|
||||
|
||||
Make sure that you have Go version 1.5 or greater (Go 1.4 will work if your version supports CGO) and that your `GOPATH` env variable is set (I recommend setting it to `~/go` if you don't have one).
|
||||
Make sure that you have Go version 1.5 or greater (Go 1.4 will work if your version supports CGO) and that your `GOPATH` env variable is set (I recommand setting it to `~/go` if you don't have one).
|
||||
|
||||
```
|
||||
go get -d github.com/zyedidia/micro/cmd/micro
|
||||
go get -d github.com/zyedidia/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`.
|
||||
|
||||
The binary will then be installed to `$GOPATH/bin` (or your `$GOBIN`).
|
||||
|
||||
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
|
||||
|
||||
If you are using MacOS, you should consider using [iTerm2](http://iterm2.com/) instead of the default Mac terminal. The iTerm2 terminal has much better mouse support as well as better handling of key events. For best keybinding behavior, choose `xterm defaults` under `Preferences->Profiles->Keys->Load Preset`. The newest versions also support true color.
|
||||
If you are using MacOS, you should consider using [iTerm2](http://iterm2.com/) instead of the default Mac terminal. The iTerm2 terminal has much better mouse support as well as better handling of key events. The newest versions also support true color.
|
||||
|
||||
### Linux clipboard support
|
||||
|
||||
@@ -178,11 +135,11 @@ that micro's default colorscheme won't look very good. You can either set
|
||||
the colorscheme to `simple`, or download a better terminal emulator, like
|
||||
mintty.
|
||||
|
||||
### Plan9, Cygwin
|
||||
### Plan9, NaCl, Cygwin
|
||||
|
||||
Please note that micro uses the amazing [tcell library](https://github.com/gdamore/tcell), but this
|
||||
means that micro is restricted to the platforms tcell supports. As a result, micro does not support
|
||||
Plan9, and Cygwin (although this may change in the future). Micro also doesn't support NaCl (but NaCl is deprecated anyways).
|
||||
Plan9, NaCl, and Cygwin (although this may change in the future).
|
||||
|
||||
# Usage
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg3336"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
width="128"
|
||||
height="128"
|
||||
viewBox="0 0 128 128"
|
||||
sodipodi:docname="logo.svg">
|
||||
<metadata
|
||||
id="metadata3342">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs3340" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1355"
|
||||
inkscape:window-height="717"
|
||||
id="namedview3338"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.6243169"
|
||||
inkscape:cx="111.32302"
|
||||
inkscape:cy="30.538264"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg3336" />
|
||||
<path
|
||||
style="fill:#2e3192;fill-opacity:1"
|
||||
d="m 56.1,127.32358 c -13.68932,-1.70993 -27.156628,-8.3544 -37.112903,-18.31068 -25.0687936,-25.068788 -25.0687936,-65.95701 0,-91.025803 25.068793,-25.0687936 65.957015,-25.0687936 91.025803,0 25.0688,25.068793 25.0688,65.957015 0,91.025803 C 95.87457,123.15123 76.198116,129.83404 56.1,127.32358 Z"
|
||||
id="path3364"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 40.756452,106.01908 c 1.442831,-1.83426 1.55476,-4.09687 0.414499,-8.37899 -0.678184,-2.546844 -0.684604,-4.05591 -0.03829,-9 1.276867,-9.767604 4.483143,-23.040636 5.565559,-23.039766 0.220979,1.74e-4 0.417725,2.092674 0.437213,4.65 0.04167,5.468298 1.558564,9.06891 4.638769,11.010942 2.551646,1.608774 9.15365,1.329324 12.80399,-0.541974 3.245124,-1.663572 7.649064,-6.112434 9.850956,-9.951438 L 76.188736,67.7 l 0.0054,3.922866 c 0.0042,2.867148 0.36894,4.642788 1.355628,6.59796 1.532058,3.035856 3.323226,4.15755 6.659322,4.17033 5.192928,0.01986 9.07014,-3.668676 10.866768,-10.338036 0.98277,-3.64821 1.064448,-11.21265 0.09235,-8.55312 -3.025218,8.276592 -4.468212,9.893562 -9.238056,10.351884 -2.629152,0.25263 -3.177804,0.08883 -4.921776,-1.469412 -1.609044,-1.437678 -2.016072,-2.308416 -2.258508,-4.8315 -0.262884,-2.73585 0.105942,-4.06497 3.32007,-11.964365 C 88.28388,40.315087 89.33625,35.536248 87,33.2 c -1.559352,-1.559353 -3.62787,-1.522741 -5.691792,0.10074 -2.295762,1.805846 -3.105984,4.070756 -5.14293,14.376662 -2.464164,12.46744 -6.525822,20.297092 -12.62193,24.331306 C 59.052142,74.98085 52.704914,73.6403 50.637191,69.282896 49.19967,66.253544 49.857706,62.552972 53.387813,53.814319 56.613526,45.829186 58.8,38.711369 58.8,36.195564 c 0,-4.161283 -4.366993,-5.665719 -7.364438,-2.537061 -2.183558,2.279144 -3.117251,5.256959 -4.280897,13.653016 -0.547956,3.953665 -1.259292,9.010489 -1.580746,11.237387 -0.321454,2.226896 -2.083918,8.706896 -3.916587,14.400002 -4.33165,13.456074 -6.85029,23.184822 -7.273674,28.096022 -0.325586,3.77675 -0.269352,4.00056 1.319044,5.25 2.187498,1.72068 3.541408,1.64679 5.05375,-0.27585 z"
|
||||
id="path3362"
|
||||
inkscape:connector-curvature="0" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -1,56 +0,0 @@
|
||||
.\" micro manual page - micro(1)
|
||||
.\"
|
||||
.\" Copyright © 2017 Zachary Yedidia <zyedidia@gmail.com>
|
||||
.\" Copyright © 2017 Collin Warren <anatoly@somethinghub.com>
|
||||
.\"
|
||||
.\" This document is provided under the same licensing as micro.
|
||||
.\" See \usr\share\doc\micro\LICENSE for more information.
|
||||
.TH micro 1 "2017-03-28"
|
||||
.SH NAME
|
||||
micro \- An intuitive and modern terminal text editor
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.B micro
|
||||
.RB []
|
||||
[
|
||||
.I "filename \&..."
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
( Copied from the README file. )
|
||||
|
||||
Micro is a terminal-based text editor that aims to be easy to use and intuitive, while also taking advantage of the full capabilities
|
||||
of modern terminals. It comes as one single, batteries-included, static binary with no dependencies.
|
||||
|
||||
As the name indicates, micro aims to be somewhat of a successor to the nano editor by being easy to install and use in a pinch, but micro also aims to be
|
||||
enjoyable to use full time, whether you work in the terminal because you prefer it (like me), or because you need to (over ssh).
|
||||
|
||||
.SH OPTIONS
|
||||
.B \-v --version
|
||||
Displays the current version of micro and the git commit hash.
|
||||
.TP
|
||||
.SH ENVIRONMENT
|
||||
Micro's behaviour can be changed by setting environment variables, of which
|
||||
there is currently only one:
|
||||
.I MICRO_TRUE_COLOR
|
||||
|
||||
When MICRO_TRUE_COLOR is set to 1, micro will attempt to treat your terminal as
|
||||
a true-color terminal and will be able to make full use of the true-color colorschemes
|
||||
that are included with micro. If MICRO_TRUE_COLOR is not set or is set to 0, then
|
||||
micro will only make use of 256 color features and will internally map true-color
|
||||
colorschemes to the nearest colors available. For more information see micro's documentation.
|
||||
|
||||
.SH NOTICE
|
||||
This manpage is intended only to serve as a quick guide to the invocation of
|
||||
micro and is not intended to replace the full documentation included with micro
|
||||
which can be accessed from within micro. Micro tells you what key combination to
|
||||
press to get help in the lower right.
|
||||
|
||||
.SH BUGS
|
||||
A comprehensive list of bugs will not be listed in this manpage. See the Github
|
||||
page at \fBhttps://github.com/zyedidia/micro/issues\fP for a list of known bugs
|
||||
and to report any newly encountered bugs you may find. We strive to correct
|
||||
bugs as swiftly as possible.
|
||||
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2017 Zachary Yedidia, Collin Warren, et al.
|
||||
See /usr/share/doc/micro/LICENSE and /usr/share/doc/micro/AUTHORS for more information.
|
||||
@@ -1,15 +0,0 @@
|
||||
[Desktop Entry]
|
||||
|
||||
Name=Micro
|
||||
GenericName=Text Editor
|
||||
Comment=Edit text files in a terminal
|
||||
|
||||
Icon=micro
|
||||
Type=Application
|
||||
Categories=terminal;TextEditor;
|
||||
Keywords=text;editor;syntax;terminal;
|
||||
|
||||
Exec=micro %U
|
||||
StartupNotify=false
|
||||
Terminal=true
|
||||
MimeType=text/plain;text/x-chdr;text/x-csrc;text/x-c++hdr;text/x-c++src;text/x-java;text/x-dsrc;text/x-pascal;text/x-perl;text/x-python;application/x-php;application/x-httpd-php3;application/x-httpd-php4;application/x-httpd-php5;application/xml;text/html;text/css;text/x-sql;text/x-diff;
|
||||
123
cmd/micro/Gopkg.lock
generated
Normal file
123
cmd/micro/Gopkg.lock
generated
Normal file
@@ -0,0 +1,123 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/blang/semver"
|
||||
packages = ["."]
|
||||
revision = "4a1e882c79dcf4ec00d2e29fac74b9c8938d5052"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/dustin/go-humanize"
|
||||
packages = ["."]
|
||||
revision = "259d2a102b871d17f30e3cd9881a642961a1e486"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/gdamore/encoding"
|
||||
packages = ["."]
|
||||
revision = "b23993cbb6353f0e6aa98d0ee318a34728f628b9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/go-errors/errors"
|
||||
packages = ["."]
|
||||
revision = "8fa88b06e5974e97fbf9899a7f86a344bfd1f105"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/lucasb-eyer/go-colorful"
|
||||
packages = ["."]
|
||||
revision = "9c2852a141bf4711e4276f8f119c90d0f20a556c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "fc9e8d8ef48496124e79ae0df75490096eccf6fe"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mattn/go-runewidth"
|
||||
packages = ["."]
|
||||
revision = "97311d9f7767e3d6f422ea06661bc2c7a19e8a5d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/go-homedir"
|
||||
packages = ["."]
|
||||
revision = "b8bc1bf767474819792c23f32d8286a45736f1c6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/sergi/go-diff"
|
||||
packages = ["diffmatchpatch"]
|
||||
revision = "feef008d51ad2b3778f85d387ccf91735543008d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/yuin/gopher-lua"
|
||||
packages = [".","ast","parse","pm"]
|
||||
revision = "b402f3114ec730d8bddb074a6c137309f561aa78"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/zyedidia/clipboard"
|
||||
packages = ["."]
|
||||
revision = "adacf416cec40266b051e7bc096c52951f2725e9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/zyedidia/glob"
|
||||
packages = ["."]
|
||||
revision = "72567a468b2481490f359cdfb015231389e0bf9d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/zyedidia/json5"
|
||||
packages = ["encoding/json5"]
|
||||
revision = "2518f8beebde6814f2d30d566260480d2ded2f76"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/zyedidia/tcell"
|
||||
packages = [".","encoding"]
|
||||
revision = "7095cc1c7f4173ae48314d80878e9985a0658889"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context"]
|
||||
revision = "007e530097ad7f954752df63046b4036f98ba6a6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "d4feaf1a7e61e1d9e79e6c4e76c6349e9cab0a03"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/text"
|
||||
packages = ["encoding","encoding/charmap","encoding/internal","encoding/internal/identifier","encoding/japanese","encoding/korean","encoding/simplifiedchinese","encoding/traditionalchinese","internal/gen","transform","unicode/cldr"]
|
||||
revision = "506f9d5c962f284575e88337e7d9296d27e729d3"
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
revision = "a83829b6f1293c91addabc89d0571c246397bbf4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "layeh.com/gopher-luar"
|
||||
packages = ["."]
|
||||
revision = "80196fe2abc5682963fc7a5261f5a5d77509938b"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "aebc6aa50d78830f86e8964c1ebc1804c1f9603477ddba606717a972ca70d261"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
123
cmd/micro/Gopkg.toml
Normal file
123
cmd/micro/Gopkg.toml
Normal file
@@ -0,0 +1,123 @@
|
||||
|
||||
## Gopkg.toml example (these lines may be deleted)
|
||||
|
||||
## "metadata" defines metadata about the project that could be used by other independent
|
||||
## systems. The metadata defined here will be ignored by dep.
|
||||
# [metadata]
|
||||
# key1 = "value that convey data to other systems"
|
||||
# system1-data = "value that is used by a system"
|
||||
# system2-data = "value that is used by another system"
|
||||
|
||||
## "required" lists a set of packages (not projects) that must be included in
|
||||
## Gopkg.lock. This list is merged with the set of packages imported by the current
|
||||
## project. Use it when your project needs a package it doesn't explicitly import -
|
||||
## including "main" packages.
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
|
||||
## "ignored" lists a set of packages (not projects) that are ignored when
|
||||
## dep statically analyzes source code. Ignored packages can be in this project,
|
||||
## or in a dependency.
|
||||
# ignored = ["github.com/user/project/badpkg"]
|
||||
|
||||
## Constraints are rules for how directly imported projects
|
||||
## may be incorporated into the depgraph. They are respected by
|
||||
## dep whether coming from the Gopkg.toml of the current project or a dependency.
|
||||
# [[constraint]]
|
||||
## Required: the root import path of the project being constrained.
|
||||
# name = "github.com/user/project"
|
||||
#
|
||||
## Recommended: the version constraint to enforce for the project.
|
||||
## Only one of "branch", "version" or "revision" can be specified.
|
||||
# version = "1.0.0"
|
||||
# branch = "master"
|
||||
# revision = "abc123"
|
||||
#
|
||||
## Optional: an alternate location (URL or import path) for the project's source.
|
||||
# source = "https://github.com/myfork/package.git"
|
||||
#
|
||||
## "metadata" defines metadata about the dependency or override that could be used
|
||||
## by other independent systems. The metadata defined here will be ignored by dep.
|
||||
# [metadata]
|
||||
# key1 = "value that convey data to other systems"
|
||||
# system1-data = "value that is used by a system"
|
||||
# system2-data = "value that is used by another system"
|
||||
|
||||
## Overrides have the same structure as [[constraint]], but supersede all
|
||||
## [[constraint]] declarations from all projects. Only [[override]] from
|
||||
## the current project's are applied.
|
||||
##
|
||||
## Overrides are a sledgehammer. Use them only as a last resort.
|
||||
# [[override]]
|
||||
## Required: the root import path of the project being constrained.
|
||||
# name = "github.com/user/project"
|
||||
#
|
||||
## Optional: specifying a version constraint override will cause all other
|
||||
## constraints on this project to be ignored; only the overridden constraint
|
||||
## need be satisfied.
|
||||
## Again, only one of "branch", "version" or "revision" can be specified.
|
||||
# version = "1.0.0"
|
||||
# branch = "master"
|
||||
# revision = "abc123"
|
||||
#
|
||||
## Optional: specifying an alternate source location as an override will
|
||||
## enforce that the alternate location is used for that project, regardless of
|
||||
## what source location any dependent projects specify.
|
||||
# source = "https://github.com/myfork/package.git"
|
||||
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/blang/semver"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/dustin/go-humanize"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/go-errors/errors"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/mattn/go-isatty"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/mattn/go-runewidth"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/go-homedir"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/sergi/go-diff"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/yuin/gopher-lua"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/zyedidia/clipboard"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/zyedidia/glob"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/zyedidia/json5"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/zyedidia/tcell"
|
||||
|
||||
[[constraint]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "layeh.com/gopher-luar"
|
||||
1255
cmd/micro/actions.go
1255
cmd/micro/actions.go
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,3 @@
|
||||
// +build linux darwin dragonfly solaris openbsd netbsd freebsd
|
||||
|
||||
package main
|
||||
|
||||
import "syscall"
|
||||
@@ -21,7 +19,8 @@ func (v *View) Suspend(usePlugin bool) bool {
|
||||
|
||||
// suspend the process
|
||||
pid := syscall.Getpid()
|
||||
err := syscall.Kill(pid, syscall.SIGSTOP)
|
||||
tid := syscall.Gettid()
|
||||
err := syscall.Tgkill(pid, tid, syscall.SIGSTOP)
|
||||
if err != nil {
|
||||
TermMessage(err)
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
// +build plan9 nacl windows
|
||||
// +build !linux
|
||||
|
||||
package main
|
||||
|
||||
func (v *View) Suspend(usePlugin bool) bool {
|
||||
messenger.Error("Suspend is only supported on Posix")
|
||||
messenger.Error("Suspend is only supported on Linux")
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
var pluginCompletions []func(string) []string
|
||||
@@ -20,9 +22,13 @@ func FileComplete(input string) (string, []string) {
|
||||
var files []os.FileInfo
|
||||
var err error
|
||||
if len(dirs) > 1 {
|
||||
home, _ := homedir.Dir()
|
||||
|
||||
directories := strings.Join(dirs[:len(dirs)-1], sep) + sep
|
||||
|
||||
directories = ReplaceHome(directories)
|
||||
if strings.HasPrefix(directories, "~") {
|
||||
directories = strings.Replace(directories, "~", home, 1)
|
||||
}
|
||||
files, err = ioutil.ReadDir(directories)
|
||||
} else {
|
||||
files, err = ioutil.ReadDir(".")
|
||||
@@ -142,63 +148,6 @@ func OptionComplete(input string) (string, []string) {
|
||||
return chosen, suggestions
|
||||
}
|
||||
|
||||
// OptionValueComplete completes values for various options
|
||||
func OptionValueComplete(inputOpt, input string) (string, []string) {
|
||||
inputOpt = strings.TrimSpace(inputOpt)
|
||||
var suggestions []string
|
||||
localSettings := DefaultLocalSettings()
|
||||
var optionVal interface{}
|
||||
for k, option := range globalSettings {
|
||||
if k == inputOpt {
|
||||
optionVal = option
|
||||
}
|
||||
}
|
||||
for k, option := range localSettings {
|
||||
if k == inputOpt {
|
||||
optionVal = option
|
||||
}
|
||||
}
|
||||
|
||||
switch optionVal.(type) {
|
||||
case bool:
|
||||
if strings.HasPrefix("on", input) {
|
||||
suggestions = append(suggestions, "on")
|
||||
} else if strings.HasPrefix("true", input) {
|
||||
suggestions = append(suggestions, "true")
|
||||
}
|
||||
if strings.HasPrefix("off", input) {
|
||||
suggestions = append(suggestions, "off")
|
||||
} else if strings.HasPrefix("false", input) {
|
||||
suggestions = append(suggestions, "false")
|
||||
}
|
||||
case string:
|
||||
switch inputOpt {
|
||||
case "colorscheme":
|
||||
_, suggestions = ColorschemeComplete(input)
|
||||
case "fileformat":
|
||||
if strings.HasPrefix("unix", input) {
|
||||
suggestions = append(suggestions, "unix")
|
||||
}
|
||||
if strings.HasPrefix("dos", input) {
|
||||
suggestions = append(suggestions, "dos")
|
||||
}
|
||||
case "sucmd":
|
||||
if strings.HasPrefix("sudo", input) {
|
||||
suggestions = append(suggestions, "sudo")
|
||||
}
|
||||
if strings.HasPrefix("doas", input) {
|
||||
suggestions = append(suggestions, "doas")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var chosen string
|
||||
if len(suggestions) == 1 {
|
||||
chosen = suggestions[0]
|
||||
}
|
||||
return chosen, suggestions
|
||||
}
|
||||
|
||||
// MakeCompletion registers a function from a plugin for autocomplete commands
|
||||
func MakeCompletion(function string) Completion {
|
||||
pluginCompletions = append(pluginCompletions, LuaFunctionComplete(function))
|
||||
@@ -220,7 +169,6 @@ func PluginComplete(complete Completion, input string) (chosen string, suggestio
|
||||
return
|
||||
}
|
||||
|
||||
// PluginCmdComplete completes with possible choices for the `> plugin` command
|
||||
func PluginCmdComplete(input string) (chosen string, suggestions []string) {
|
||||
for _, cmd := range []string{"install", "remove", "search", "update", "list"} {
|
||||
if strings.HasPrefix(cmd, input) {
|
||||
@@ -234,7 +182,6 @@ func PluginCmdComplete(input string) (chosen string, suggestions []string) {
|
||||
return chosen, suggestions
|
||||
}
|
||||
|
||||
// PluginnameComplete completes with the names of loaded plugins
|
||||
func PluginNameComplete(input string) (chosen string, suggestions []string) {
|
||||
for _, pp := range GetAllPluginPackages() {
|
||||
if strings.HasPrefix(pp.Name, input) {
|
||||
|
||||
@@ -1,133 +1,101 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/flynn/json5"
|
||||
"github.com/zyedidia/json5/encoding/json5"
|
||||
"github.com/zyedidia/tcell"
|
||||
)
|
||||
|
||||
var bindingsStr map[string]string
|
||||
var bindings map[Key][]func(*View, bool) bool
|
||||
var mouseBindings map[Key][]func(*View, bool, *tcell.EventMouse) bool
|
||||
var helpBinding string
|
||||
var kmenuBinding string
|
||||
|
||||
var mouseBindingActions = map[string]func(*View, bool, *tcell.EventMouse) bool{
|
||||
"MousePress": (*View).MousePress,
|
||||
"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,
|
||||
"SelectLine": (*View).SelectLine,
|
||||
"SelectToStartOfLine": (*View).SelectToStartOfLine,
|
||||
"SelectToEndOfLine": (*View).SelectToEndOfLine,
|
||||
"ParagraphPrevious": (*View).ParagraphPrevious,
|
||||
"ParagraphNext": (*View).ParagraphNext,
|
||||
"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,
|
||||
"ToggleKeyMenu": (*View).ToggleKeyMenu,
|
||||
"ToggleRuler": (*View).ToggleRuler,
|
||||
"JumpLine": (*View).JumpLine,
|
||||
"ClearStatus": (*View).ClearStatus,
|
||||
"ShellMode": (*View).ShellMode,
|
||||
"CommandMode": (*View).CommandMode,
|
||||
"ToggleOverwriteMode": (*View).ToggleOverwriteMode,
|
||||
"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,
|
||||
"JumpToMatchingBrace": (*View).JumpToMatchingBrace,
|
||||
"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,
|
||||
|
||||
// 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,
|
||||
@@ -247,8 +215,6 @@ 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,
|
||||
@@ -264,16 +230,12 @@ var bindingKeys = map[string]tcell.Key{
|
||||
type Key struct {
|
||||
keyCode tcell.Key
|
||||
modifiers tcell.ModMask
|
||||
buttons tcell.ButtonMask
|
||||
r rune
|
||||
escape string
|
||||
}
|
||||
|
||||
// InitBindings initializes the keybindings for micro
|
||||
func InitBindings() {
|
||||
bindings = make(map[Key][]func(*View, bool) bool)
|
||||
bindingsStr = make(map[string]string)
|
||||
mouseBindings = make(map[Key][]func(*View, bool, *tcell.EventMouse) bool)
|
||||
|
||||
var parsed map[string]string
|
||||
defaults := DefaultBindings()
|
||||
@@ -324,14 +286,6 @@ modSearch:
|
||||
case strings.HasPrefix(k, "Shift"):
|
||||
k = k[5:]
|
||||
modifiers |= tcell.ModShift
|
||||
case strings.HasPrefix(k, "\x1b"):
|
||||
return Key{
|
||||
keyCode: -1,
|
||||
modifiers: modifiers,
|
||||
buttons: -1,
|
||||
r: 0,
|
||||
escape: k,
|
||||
}, true
|
||||
default:
|
||||
break modSearch
|
||||
}
|
||||
@@ -342,13 +296,11 @@ modSearch:
|
||||
// first.
|
||||
if modifiers&tcell.ModCtrl != 0 {
|
||||
// see if the key is in bindingKeys with the Ctrl prefix.
|
||||
k = string(unicode.ToUpper(rune(k[0]))) + k[1:]
|
||||
if code, ok := bindingKeys["Ctrl"+k]; ok {
|
||||
// It is, we're done.
|
||||
return Key{
|
||||
keyCode: code,
|
||||
modifiers: modifiers,
|
||||
buttons: -1,
|
||||
r: 0,
|
||||
}, true
|
||||
}
|
||||
@@ -359,16 +311,6 @@ 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
|
||||
}
|
||||
@@ -378,13 +320,12 @@ modSearch:
|
||||
return Key{
|
||||
keyCode: tcell.KeyRune,
|
||||
modifiers: modifiers,
|
||||
buttons: -1,
|
||||
r: rune(k[0]),
|
||||
}, true
|
||||
}
|
||||
|
||||
// We don't know what happened.
|
||||
return Key{buttons: -1}, false
|
||||
return Key{}, false
|
||||
}
|
||||
|
||||
// findAction will find 'action' using string 'v'
|
||||
@@ -398,53 +339,6 @@ 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
|
||||
}
|
||||
|
||||
// TryBindKey tries to bind a key by writing to configDir/bindings.json
|
||||
// This function is unused for now
|
||||
func TryBindKey(k, v string) {
|
||||
filename := configDir + "/bindings.json"
|
||||
if _, e := os.Stat(filename); e == nil {
|
||||
input, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
TermMessage("Error reading bindings.json file: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
conflict := -1
|
||||
lines := strings.Split(string(input), "\n")
|
||||
for i, l := range lines {
|
||||
parts := strings.Split(l, ":")
|
||||
if len(parts) >= 2 {
|
||||
if strings.Contains(parts[0], k) {
|
||||
conflict = i
|
||||
TermMessage("Warning: Keybinding conflict:", k, " has been overwritten")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding := fmt.Sprintf(" \"%s\": \"%s\",", k, v)
|
||||
if conflict == -1 {
|
||||
lines = append([]string{lines[0], binding}, lines[conflict:]...)
|
||||
} else {
|
||||
lines = append(append(lines[:conflict], binding), lines[conflict+1:]...)
|
||||
}
|
||||
txt := strings.Join(lines, "\n")
|
||||
err = ioutil.WriteFile(filename, []byte(txt), 0644)
|
||||
if err != nil {
|
||||
TermMessage("Error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BindKey takes a key and an action and binds the two together
|
||||
func BindKey(k, v string) {
|
||||
key, ok := findKey(k)
|
||||
@@ -455,52 +349,25 @@ func BindKey(k, v string) {
|
||||
if v == "ToggleHelp" {
|
||||
helpBinding = k
|
||||
}
|
||||
if v == "ToggleKeyMenu" {
|
||||
kmenuBinding = k
|
||||
}
|
||||
if helpBinding == k && v != "ToggleHelp" {
|
||||
helpBinding = ""
|
||||
}
|
||||
if kmenuBinding == k && v != "ToggleKeyMenu" {
|
||||
kmenuBinding = ""
|
||||
}
|
||||
|
||||
actionNames := strings.Split(v, ",")
|
||||
if actionNames[0] == "UnbindKey" {
|
||||
delete(bindings, key)
|
||||
delete(mouseBindings, key)
|
||||
delete(bindingsStr, k)
|
||||
if len(actionNames) == 1 {
|
||||
return
|
||||
actionNames = make([]string, 0, 0)
|
||||
} else {
|
||||
actionNames = append(actionNames[:0], actionNames[1:]...)
|
||||
}
|
||||
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 {
|
||||
if strings.HasPrefix(actionName, "Mouse") {
|
||||
mouseActions = append(mouseActions, findMouseAction(actionName))
|
||||
} else if strings.HasPrefix(actionName, "command:") {
|
||||
cmd := strings.SplitN(actionName, ":", 2)[1]
|
||||
actions = append(actions, CommandAction(cmd))
|
||||
} else if strings.HasPrefix(actionName, "command-edit:") {
|
||||
cmd := strings.SplitN(actionName, ":", 2)[1]
|
||||
actions = append(actions, CommandEditAction(cmd))
|
||||
} else {
|
||||
actions = append(actions, findAction(actionName))
|
||||
}
|
||||
actions = append(actions, findAction(actionName))
|
||||
}
|
||||
|
||||
if len(actions) > 0 {
|
||||
// Can't have a binding be both mouse and normal
|
||||
delete(mouseBindings, key)
|
||||
bindings[key] = actions
|
||||
bindingsStr[k] = v
|
||||
} else if len(mouseActions) > 0 {
|
||||
// Can't have a binding be both mouse and normal
|
||||
delete(bindings, key)
|
||||
mouseBindings[key] = mouseActions
|
||||
}
|
||||
bindings[key] = actions
|
||||
}
|
||||
|
||||
// DefaultBindings returns a map containing micro's default keybindings
|
||||
@@ -530,8 +397,6 @@ func DefaultBindings() map[string]string {
|
||||
"CtrlDown": "CursorEnd",
|
||||
"CtrlShiftUp": "SelectToStart",
|
||||
"CtrlShiftDown": "SelectToEnd",
|
||||
"Alt-{": "ParagraphPrevious",
|
||||
"Alt-}": "ParagraphNext",
|
||||
"Enter": "InsertNewline",
|
||||
"CtrlH": "Backspace",
|
||||
"Backspace": "Backspace",
|
||||
@@ -561,10 +426,7 @@ func DefaultBindings() map[string]string {
|
||||
"CtrlEnd": "CursorEnd",
|
||||
"PageUp": "CursorPageUp",
|
||||
"PageDown": "CursorPageDown",
|
||||
"CtrlPageUp": "PreviousTab",
|
||||
"CtrlPageDown": "NextTab",
|
||||
"CtrlG": "ToggleHelp",
|
||||
"Alt-g": "ToggleKeyMenu",
|
||||
"CtrlR": "ToggleRuler",
|
||||
"CtrlL": "JumpLine",
|
||||
"Delete": "Delete",
|
||||
@@ -574,34 +436,22 @@ func DefaultBindings() map[string]string {
|
||||
"CtrlW": "NextSplit",
|
||||
"CtrlU": "ToggleMacro",
|
||||
"CtrlJ": "PlayMacro",
|
||||
"Insert": "ToggleOverwriteMode",
|
||||
|
||||
// Emacs-style keybindings
|
||||
"Alt-f": "WordRight",
|
||||
"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",
|
||||
"F2": "Save",
|
||||
"F3": "Find",
|
||||
"F4": "Quit",
|
||||
"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",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -17,16 +15,10 @@ import (
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/zyedidia/micro/cmd/micro/highlight"
|
||||
)
|
||||
|
||||
var (
|
||||
// 0 - no line type detected
|
||||
// 1 - lf detected
|
||||
// 2 - crlf detected
|
||||
fileformat = 0
|
||||
)
|
||||
|
||||
// Buffer stores the text for files that are loaded into the text editor
|
||||
// It uses a rope to efficiently store the string and contains some
|
||||
// simple functions for saving and wrapper functions for modifying the rope
|
||||
@@ -36,9 +28,7 @@ type Buffer struct {
|
||||
// This stores all the text in the buffer as an array of lines
|
||||
*LineArray
|
||||
|
||||
Cursor Cursor
|
||||
cursors []*Cursor // for multiple cursors
|
||||
curCursor int // the current cursor
|
||||
Cursor Cursor
|
||||
|
||||
// Path to the file on disk
|
||||
Path string
|
||||
@@ -53,15 +43,11 @@ type Buffer struct {
|
||||
// Stores the last modification time of the file the buffer is pointing to
|
||||
ModTime time.Time
|
||||
|
||||
// NumLines is the number of lines in the buffer
|
||||
NumLines int
|
||||
|
||||
syntaxDef *highlight.Def
|
||||
highlighter *highlight.Highlighter
|
||||
|
||||
// Hash of the original buffer -- empty if fastdirty is on
|
||||
origHash [16]byte
|
||||
|
||||
// Buffer local settings
|
||||
Settings map[string]interface{}
|
||||
}
|
||||
@@ -74,8 +60,6 @@ type SerializedBuffer struct {
|
||||
ModTime time.Time
|
||||
}
|
||||
|
||||
// NewBufferFromString creates a new buffer containing the given
|
||||
// string
|
||||
func NewBufferFromString(text, path string) *Buffer {
|
||||
return NewBuffer(strings.NewReader(text), int64(len(text)), path)
|
||||
}
|
||||
@@ -102,12 +86,6 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
if fileformat == 1 {
|
||||
b.Settings["fileformat"] = "unix"
|
||||
} else if fileformat == 2 {
|
||||
b.Settings["fileformat"] = "dos"
|
||||
}
|
||||
|
||||
absPath, _ := filepath.Abs(path)
|
||||
|
||||
b.Path = path
|
||||
@@ -162,7 +140,7 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
|
||||
|
||||
InitLocalSettings(b)
|
||||
|
||||
if len(*flagStartPos) == 0 && (b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool)) {
|
||||
if b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool) {
|
||||
// If either savecursor or saveundo is turned on, we need to load the serialized information
|
||||
// from ~/.config/micro/buffers
|
||||
file, err := os.Open(configDir + "/buffers/" + EscapePath(b.AbsPath))
|
||||
@@ -181,7 +159,7 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
|
||||
}
|
||||
|
||||
if b.Settings["saveundo"].(bool) {
|
||||
// We should only use last time's eventhandler if the file wasn't modified by someone else in the meantime
|
||||
// We should only use last time's eventhandler if the file wasn't by someone else in the meantime
|
||||
if b.ModTime == buffer.ModTime {
|
||||
b.EventHandler = buffer.EventHandler
|
||||
b.EventHandler.buf = b
|
||||
@@ -191,22 +169,9 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
|
||||
file.Close()
|
||||
}
|
||||
|
||||
if !b.Settings["fastdirty"].(bool) {
|
||||
if size > 50000 {
|
||||
// If the file is larger than a megabyte fastdirty needs to be on
|
||||
b.Settings["fastdirty"] = true
|
||||
} else {
|
||||
b.origHash = md5.Sum([]byte(b.String()))
|
||||
}
|
||||
}
|
||||
|
||||
b.cursors = []*Cursor{&b.Cursor}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// GetName returns the name that should be displayed in the statusline
|
||||
// for this buffer
|
||||
func (b *Buffer) GetName() string {
|
||||
if b.name == "" {
|
||||
if b.Path == "" {
|
||||
@@ -239,7 +204,7 @@ func (b *Buffer) UpdateRules() {
|
||||
}
|
||||
|
||||
ft := b.Settings["filetype"].(string)
|
||||
if (ft == "Unknown" || ft == "") && !rehighlight {
|
||||
if ft == "Unknown" || ft == "" {
|
||||
if highlight.MatchFiletype(ftdetect, b.Path, b.lines[0].data) {
|
||||
header := new(highlight.Header)
|
||||
header.FileType = file.FileType
|
||||
@@ -252,7 +217,7 @@ func (b *Buffer) UpdateRules() {
|
||||
rehighlight = true
|
||||
}
|
||||
} else {
|
||||
if file.FileType == ft && !rehighlight {
|
||||
if file.FileType == ft {
|
||||
header := new(highlight.Header)
|
||||
header.FileType = file.FileType
|
||||
header.FtDetect = ftdetect
|
||||
@@ -271,6 +236,7 @@ func (b *Buffer) UpdateRules() {
|
||||
if b.syntaxDef != nil {
|
||||
highlight.ResolveIncludes(b.syntaxDef, files)
|
||||
}
|
||||
files = nil
|
||||
|
||||
if b.highlighter == nil || rehighlight {
|
||||
if b.syntaxDef != nil {
|
||||
@@ -339,41 +305,6 @@ func (b *Buffer) Update() {
|
||||
b.NumLines = len(b.lines)
|
||||
}
|
||||
|
||||
// MergeCursors merges any cursors that are at the same position
|
||||
// into one cursor
|
||||
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
|
||||
|
||||
for i := range b.cursors {
|
||||
b.cursors[i].Num = i
|
||||
}
|
||||
|
||||
if b.curCursor >= len(b.cursors) {
|
||||
b.curCursor = len(b.cursors) - 1
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateCursors updates all the cursors indicies
|
||||
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)
|
||||
@@ -397,7 +328,7 @@ func (b *Buffer) Serialize() error {
|
||||
b.ModTime,
|
||||
})
|
||||
}
|
||||
err = file.Close()
|
||||
file.Close()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -406,6 +337,7 @@ func (b *Buffer) Serialize() error {
|
||||
// SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
|
||||
func (b *Buffer) SaveAs(filename string) error {
|
||||
b.UpdateRules()
|
||||
dir, _ := homedir.Dir()
|
||||
if b.Settings["rmtrailingws"].(bool) {
|
||||
r, _ := regexp.Compile(`[ \t]+$`)
|
||||
for lineNum, line := range b.Lines(0, b.NumLines) {
|
||||
@@ -424,60 +356,17 @@ func (b *Buffer) SaveAs(filename string) error {
|
||||
b.Insert(end, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
str := b.String()
|
||||
data := []byte(str)
|
||||
err := ioutil.WriteFile(filename, data, 0644)
|
||||
if err == nil {
|
||||
b.Path = strings.Replace(filename, "~", dir, 1)
|
||||
b.IsModified = false
|
||||
b.ModTime, _ = GetModTime(filename)
|
||||
}()
|
||||
|
||||
// Removes any tilde and replaces with the absolute path to home
|
||||
var absFilename string = ReplaceHome(filename)
|
||||
|
||||
// Get the leading path to the file | "." is returned if there's no leading path provided
|
||||
if dirname := filepath.Dir(absFilename); dirname != "." {
|
||||
// Check if the parent dirs don't exist
|
||||
if _, statErr := os.Stat(dirname); os.IsNotExist(statErr) {
|
||||
// Prompt to make sure they want to create the dirs that are missing
|
||||
if yes, canceled := messenger.YesNoPrompt("Parent folders \"" + dirname + "\" do not exist. Create them? (y,n)"); yes && !canceled {
|
||||
// Create all leading dir(s) since they don't exist
|
||||
if mkdirallErr := os.MkdirAll(dirname, os.ModePerm); mkdirallErr != nil {
|
||||
// If there was an error creating the dirs
|
||||
return mkdirallErr
|
||||
}
|
||||
} else {
|
||||
// If they canceled the creation of leading dirs
|
||||
return errors.New("Save aborted")
|
||||
}
|
||||
}
|
||||
return b.Serialize()
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(absFilename, os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.Truncate(0); err != nil {
|
||||
return err
|
||||
}
|
||||
useCrlf := b.Settings["fileformat"] == "dos"
|
||||
for i, l := range b.lines {
|
||||
if _, err := f.Write(l.data); err != nil {
|
||||
return err
|
||||
}
|
||||
if i != len(b.lines)-1 {
|
||||
if useCrlf {
|
||||
if _, err := f.Write([]byte{'\r', '\n'}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := f.Write([]byte{'\n'}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b.Path = filename
|
||||
b.IsModified = false
|
||||
return b.Serialize()
|
||||
b.ModTime, _ = GetModTime(filename)
|
||||
return err
|
||||
}
|
||||
|
||||
// SaveAsWithSudo is the same as SaveAs except it uses a neat trick
|
||||
@@ -486,13 +375,21 @@ func (b *Buffer) SaveAsWithSudo(filename string) error {
|
||||
b.UpdateRules()
|
||||
b.Path = filename
|
||||
|
||||
// Shut down the screen because we're going to interact directly with the shell
|
||||
screen.Fini()
|
||||
screen = nil
|
||||
// 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
|
||||
}
|
||||
|
||||
// Set up everything for the command
|
||||
cmd := exec.Command(globalSettings["sucmd"].(string), "tee", filename)
|
||||
cmd.Stdin = bytes.NewBufferString(b.SaveString(b.Settings["fileformat"] == "dos"))
|
||||
cmd := exec.Command("sudo", "tee", filename)
|
||||
cmd.Stdin = bytes.NewBufferString(b.String())
|
||||
|
||||
// This is a trap for Ctrl-C so that it doesn't kill micro
|
||||
// Instead we trap Ctrl-C to kill the program we're running
|
||||
@@ -506,10 +403,13 @@ func (b *Buffer) SaveAsWithSudo(filename string) error {
|
||||
|
||||
// Start the command
|
||||
cmd.Start()
|
||||
err := cmd.Wait()
|
||||
err = cmd.Wait()
|
||||
|
||||
// Start the screen back up
|
||||
InitScreen()
|
||||
// If we needed the password, we closed the screen, so we have to initialize it again
|
||||
if needPassword {
|
||||
// Start the screen back up
|
||||
InitScreen()
|
||||
}
|
||||
if err == nil {
|
||||
b.IsModified = false
|
||||
b.ModTime, _ = GetModTime(filename)
|
||||
@@ -518,15 +418,6 @@ func (b *Buffer) SaveAsWithSudo(filename string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Modified returns if this buffer has been modified since
|
||||
// being opened
|
||||
func (b *Buffer) Modified() bool {
|
||||
if b.Settings["fastdirty"].(bool) {
|
||||
return b.IsModified
|
||||
}
|
||||
return b.origHash != md5.Sum([]byte(b.String()))
|
||||
}
|
||||
|
||||
func (b *Buffer) insert(pos Loc, value []byte) {
|
||||
b.IsModified = true
|
||||
b.LineArray.insert(pos, value)
|
||||
@@ -571,7 +462,6 @@ func (b *Buffer) Line(n int) string {
|
||||
return string(b.lines[n].data)
|
||||
}
|
||||
|
||||
// LinesNum returns the number of lines in the buffer
|
||||
func (b *Buffer) LinesNum() int {
|
||||
return len(b.lines)
|
||||
}
|
||||
@@ -643,67 +533,3 @@ func (b *Buffer) ClearMatches() {
|
||||
b.SetState(i, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) clearCursors() {
|
||||
for i := 1; i < len(b.cursors); i++ {
|
||||
b.cursors[i] = nil
|
||||
}
|
||||
b.cursors = b.cursors[:1]
|
||||
b.UpdateCursors()
|
||||
b.Cursor.ResetSelection()
|
||||
}
|
||||
|
||||
var bracePairs = [][2]rune{
|
||||
[2]rune{'(', ')'},
|
||||
[2]rune{'{', '}'},
|
||||
[2]rune{'[', ']'},
|
||||
}
|
||||
|
||||
// FindMatchingBrace returns the location in the buffer of the matching bracket
|
||||
// It is given a brace type containing the open and closing character, (for example
|
||||
// '{' and '}') as well as the location to match from
|
||||
func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) Loc {
|
||||
curLine := []rune(string(b.lines[start.Y].data))
|
||||
startChar := curLine[start.X]
|
||||
var i int
|
||||
if startChar == braceType[0] {
|
||||
for y := start.Y; y < b.NumLines; y++ {
|
||||
l := []rune(string(b.lines[y].data))
|
||||
xInit := 0
|
||||
if y == start.Y {
|
||||
xInit = start.X
|
||||
}
|
||||
for x := xInit; x < len(l); x++ {
|
||||
r := l[x]
|
||||
if r == braceType[0] {
|
||||
i++
|
||||
} else if r == braceType[1] {
|
||||
i--
|
||||
if i == 0 {
|
||||
return Loc{x, y}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if startChar == braceType[1] {
|
||||
for y := start.Y; y >= 0; y-- {
|
||||
l := []rune(string(b.lines[y].data))
|
||||
xInit := len(l) - 1
|
||||
if y == start.Y {
|
||||
xInit = start.X
|
||||
}
|
||||
for x := xInit; x >= 0; x-- {
|
||||
r := l[x]
|
||||
if r == braceType[0] {
|
||||
i--
|
||||
if i == 0 {
|
||||
return Loc{x, y}
|
||||
}
|
||||
} else if r == braceType[1] {
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return start
|
||||
}
|
||||
|
||||
@@ -65,21 +65,6 @@ type CellView struct {
|
||||
}
|
||||
|
||||
func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
|
||||
if width <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
matchingBrace := Loc{-1, -1}
|
||||
// bracePairs is defined in buffer.go
|
||||
if buf.Settings["matchbrace"].(bool) {
|
||||
for _, bp := range bracePairs {
|
||||
r := buf.Cursor.RuneUnder(buf.Cursor.X)
|
||||
if r == bp[0] || r == bp[1] {
|
||||
matchingBrace = buf.FindMatchingBrace(bp, buf.Cursor.Loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tabsize := int(buf.Settings["tabsize"].(float64))
|
||||
softwrap := buf.Settings["softwrap"].(bool)
|
||||
indentrunes := []rune(buf.Settings["indentchar"].(string))
|
||||
@@ -148,13 +133,7 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
|
||||
char := line[colN]
|
||||
|
||||
if viewCol >= 0 {
|
||||
st := curStyle
|
||||
if colN == matchingBrace.X && lineN == matchingBrace.Y && !buf.Cursor.HasSelection() {
|
||||
st = curStyle.Reverse(true)
|
||||
}
|
||||
if viewCol < len(c.lines[viewLine]) {
|
||||
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, char, st, 1}
|
||||
}
|
||||
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, char, curStyle, 1}
|
||||
}
|
||||
if char == '\t' {
|
||||
charWidth := tabsize - (viewCol+left)%tabsize
|
||||
@@ -163,8 +142,7 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
|
||||
c.lines[viewLine][viewCol].width = charWidth
|
||||
|
||||
indentStyle := curStyle
|
||||
ch := buf.Settings["indentchar"].(string)
|
||||
if group, ok := colorscheme["indent-char"]; ok && !IsStrWhitespace(ch) && ch != "" {
|
||||
if group, ok := colorscheme["indent-char"]; ok {
|
||||
indentStyle = group
|
||||
}
|
||||
|
||||
@@ -173,7 +151,7 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
|
||||
|
||||
for i := 1; i < charWidth; i++ {
|
||||
viewCol++
|
||||
if viewCol >= 0 && viewCol < lineLength && viewCol < len(c.lines[viewLine]) {
|
||||
if viewCol >= 0 && viewCol < lineLength {
|
||||
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
|
||||
}
|
||||
}
|
||||
@@ -185,7 +163,7 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
|
||||
}
|
||||
for i := 1; i < charWidth; i++ {
|
||||
viewCol++
|
||||
if viewCol >= 0 && viewCol < lineLength && viewCol < len(c.lines[viewLine]) {
|
||||
if viewCol >= 0 && viewCol < lineLength {
|
||||
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type Colorscheme map[string]tcell.Style
|
||||
// The current colorscheme
|
||||
var colorscheme Colorscheme
|
||||
|
||||
// GetColor takes in a syntax group and returns the colorscheme's style for that group
|
||||
// This takes in a syntax group and returns the colorscheme's style for that group
|
||||
func GetColor(color string) tcell.Style {
|
||||
st := defStyle
|
||||
if color == "" {
|
||||
@@ -54,7 +54,7 @@ func InitColorscheme() {
|
||||
Foreground(tcell.ColorDefault).
|
||||
Background(tcell.ColorDefault)
|
||||
if screen != nil {
|
||||
// screen.SetStyle(defStyle)
|
||||
screen.SetStyle(defStyle)
|
||||
}
|
||||
|
||||
LoadDefaultColorscheme()
|
||||
@@ -109,7 +109,7 @@ func ParseColorscheme(text string) Colorscheme {
|
||||
defStyle = style
|
||||
}
|
||||
if screen != nil {
|
||||
// screen.SetStyle(defStyle)
|
||||
screen.SetStyle(defStyle)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Color-link statement is not valid:", line)
|
||||
@@ -252,9 +252,5 @@ func GetColor256(color int) tcell.Color {
|
||||
tcell.Color253, tcell.Color254, tcell.Color255,
|
||||
}
|
||||
|
||||
if color >= 0 && color < len(colors) {
|
||||
return colors[color]
|
||||
}
|
||||
|
||||
return tcell.ColorDefault
|
||||
return colors[color]
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
@@ -10,7 +14,7 @@ import (
|
||||
"strings"
|
||||
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
"github.com/zyedidia/micro/cmd/micro/shellwords"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// A Command contains a action (a function to call) as well as information about how to autocomplete the command
|
||||
@@ -31,32 +35,27 @@ var commandActions map[string]func([]string)
|
||||
|
||||
func init() {
|
||||
commandActions = map[string]func([]string){
|
||||
"Set": Set,
|
||||
"SetLocal": SetLocal,
|
||||
"Show": Show,
|
||||
"ShowKey": ShowKey,
|
||||
"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,
|
||||
"Term": Term,
|
||||
"MemUsage": MemUsage,
|
||||
"Retab": Retab,
|
||||
"Raw": Raw,
|
||||
"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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,54 +89,27 @@ 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, OptionValueCompletion}},
|
||||
"setlocal": {"SetLocal", []Completion{OptionCompletion, OptionValueCompletion}},
|
||||
"show": {"Show", []Completion{OptionCompletion, NoCompletion}},
|
||||
"showkey": {"ShowKey", []Completion{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}},
|
||||
"term": {"Term", []Completion{NoCompletion}},
|
||||
"memusage": {"MemUsage", []Completion{NoCompletion}},
|
||||
"retab": {"Retab", []Completion{NoCompletion}},
|
||||
"raw": {"Raw", []Completion{NoCompletion}},
|
||||
}
|
||||
}
|
||||
|
||||
// CommandEditAction returns a bindable function that opens a prompt with
|
||||
// the given string and executes the command when the user presses
|
||||
// enter
|
||||
func CommandEditAction(prompt string) func(*View, bool) bool {
|
||||
return func(v *View, usePlugin bool) bool {
|
||||
input, canceled := messenger.Prompt("> ", prompt, "Command", CommandCompletion)
|
||||
if !canceled {
|
||||
HandleCommand(input)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// CommandAction returns a bindable function which executes the
|
||||
// given command
|
||||
func CommandAction(cmd string) func(*View, bool) bool {
|
||||
return func(v *View, usePlugin bool) bool {
|
||||
HandleCommand(cmd)
|
||||
return false
|
||||
"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}},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,34 +196,6 @@ func PluginCmd(args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Retab changes all spaces to tabs or all tabs to spaces
|
||||
// depending on the user's settings
|
||||
func Retab(args []string) {
|
||||
CurView().Retab(true)
|
||||
}
|
||||
|
||||
// Raw opens a new raw view which displays the escape sequences micro
|
||||
// is receiving in real-time
|
||||
func Raw(args []string) {
|
||||
buf := NewBufferFromString("", "Raw events")
|
||||
|
||||
view := NewView(buf)
|
||||
view.Buf.Insert(view.Cursor.Loc, "Warning: Showing raw event escape codes\n")
|
||||
view.Buf.Insert(view.Cursor.Loc, "Use CtrlQ to exit\n")
|
||||
view.Type = vtRaw
|
||||
tab := NewTabFromView(view)
|
||||
tab.SetNum(len(tabs))
|
||||
tabs = append(tabs, tab)
|
||||
curTab = len(tabs) - 1
|
||||
if len(tabs) == 2 {
|
||||
for _, t := range tabs {
|
||||
for _, v := range t.views {
|
||||
v.ToggleTabbar()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TabSwitch switches to a given tab either by name or by number
|
||||
func TabSwitch(args []string) {
|
||||
if len(args) > 0 {
|
||||
@@ -284,19 +228,12 @@ func TabSwitch(args []string) {
|
||||
// Cd changes the current working directory
|
||||
func Cd(args []string) {
|
||||
if len(args) > 0 {
|
||||
path := ReplaceHome(args[0])
|
||||
err := os.Chdir(path)
|
||||
if err != nil {
|
||||
messenger.Error("Error with cd: ", err)
|
||||
return
|
||||
}
|
||||
wd, _ := os.Getwd()
|
||||
home, _ := homedir.Dir()
|
||||
path := strings.Replace(args[0], "~", home, 1)
|
||||
os.Chdir(path)
|
||||
for _, tab := range tabs {
|
||||
for _, view := range tab.views {
|
||||
if len(view.Buf.name) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
view.Buf.Path, _ = MakeRelative(view.Buf.AbsPath, wd)
|
||||
if p, _ := filepath.Abs(view.Buf.Path); !strings.Contains(p, wd) {
|
||||
view.Buf.Path = view.Buf.AbsPath
|
||||
@@ -335,12 +272,7 @@ func Open(args []string) {
|
||||
if len(args) > 0 {
|
||||
filename := args[0]
|
||||
// the filename might or might not be quoted, so unquote first then join the strings.
|
||||
args, err := shellwords.Split(filename)
|
||||
if err != nil {
|
||||
messenger.Error("Error parsing args ", err)
|
||||
return
|
||||
}
|
||||
filename = strings.Join(args, " ")
|
||||
filename = strings.Join(SplitCommandArgs(filename), " ")
|
||||
|
||||
CurView().Open(filename)
|
||||
} else {
|
||||
@@ -391,7 +323,8 @@ func VSplit(args []string) {
|
||||
CurView().VSplit(NewBufferFromString("", ""))
|
||||
} else {
|
||||
filename := args[0]
|
||||
filename = ReplaceHome(filename)
|
||||
home, _ := homedir.Dir()
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
file, err := os.Open(filename)
|
||||
fileInfo, _ := os.Stat(filename)
|
||||
|
||||
@@ -420,7 +353,8 @@ func HSplit(args []string) {
|
||||
CurView().HSplit(NewBufferFromString("", ""))
|
||||
} else {
|
||||
filename := args[0]
|
||||
filename = ReplaceHome(filename)
|
||||
home, _ := homedir.Dir()
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
file, err := os.Open(filename)
|
||||
fileInfo, _ := os.Stat(filename)
|
||||
|
||||
@@ -460,7 +394,8 @@ func NewTab(args []string) {
|
||||
CurView().AddTab(true)
|
||||
} else {
|
||||
filename := args[0]
|
||||
filename = ReplaceHome(filename)
|
||||
home, _ := homedir.Dir()
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
file, err := os.Open(filename)
|
||||
fileInfo, _ := os.Stat(filename)
|
||||
|
||||
@@ -538,20 +473,6 @@ func Show(args []string) {
|
||||
messenger.Message(option)
|
||||
}
|
||||
|
||||
// ShowKey displays the action that a key is bound to
|
||||
func ShowKey(args []string) {
|
||||
if len(args) < 1 {
|
||||
messenger.Error("Please provide a key to show")
|
||||
return
|
||||
}
|
||||
|
||||
if action, ok := bindingsStr[args[0]]; ok {
|
||||
messenger.Message(action)
|
||||
} else {
|
||||
messenger.Message(args[0], " has no binding")
|
||||
}
|
||||
}
|
||||
|
||||
// Bind creates a new keybinding
|
||||
func Bind(args []string) {
|
||||
if len(args) < 2 {
|
||||
@@ -564,7 +485,7 @@ func Bind(args []string) {
|
||||
// Run runs a shell command in the background
|
||||
func Run(args []string) {
|
||||
// Run a shell command in the background (openTerm is false)
|
||||
HandleShellCommand(shellwords.Join(args...), false, true)
|
||||
HandleShellCommand(JoinCommandArgs(args...), false, true)
|
||||
}
|
||||
|
||||
// Quit closes the main view
|
||||
@@ -585,35 +506,19 @@ func Save(args []string) {
|
||||
|
||||
// Replace runs search and replace
|
||||
func Replace(args []string) {
|
||||
if len(args) < 2 || len(args) > 4 {
|
||||
if len(args) < 2 {
|
||||
// We need to find both a search and replace expression
|
||||
messenger.Error("Invalid replace statement: " + strings.Join(args, " "))
|
||||
return
|
||||
}
|
||||
|
||||
all := false
|
||||
noRegex := false
|
||||
|
||||
if len(args) > 2 {
|
||||
for _, arg := range args[2:] {
|
||||
switch arg {
|
||||
case "-a":
|
||||
all = true
|
||||
case "-l":
|
||||
noRegex = true
|
||||
default:
|
||||
messenger.Error("Invalid flag: " + arg)
|
||||
return
|
||||
}
|
||||
}
|
||||
var flags string
|
||||
if len(args) == 3 {
|
||||
// The user included some flags
|
||||
flags = args[2]
|
||||
}
|
||||
|
||||
search := string(args[0])
|
||||
|
||||
if noRegex {
|
||||
search = regexp.QuoteMeta(search)
|
||||
}
|
||||
|
||||
replace := string(args[1])
|
||||
|
||||
regex, err := regexp.Compile("(?m)" + search)
|
||||
@@ -626,32 +531,7 @@ func Replace(args []string) {
|
||||
view := CurView()
|
||||
|
||||
found := 0
|
||||
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 all {
|
||||
replaceAll()
|
||||
} else {
|
||||
if strings.Contains(flags, "c") {
|
||||
for {
|
||||
// The 'check' flag was used
|
||||
Search(search, view, true)
|
||||
@@ -660,7 +540,7 @@ func Replace(args []string) {
|
||||
}
|
||||
view.Relocate()
|
||||
RedrawAll()
|
||||
choice, canceled := messenger.LetterPrompt("Perform replacement? (y,n,a)", 'y', 'n', 'a')
|
||||
choice, canceled := messenger.YesNoPrompt("Perform replacement? (y,n)")
|
||||
if canceled {
|
||||
if view.Cursor.HasSelection() {
|
||||
view.Cursor.Loc = view.Cursor.CurSelection[0]
|
||||
@@ -668,27 +548,42 @@ func Replace(args []string) {
|
||||
}
|
||||
messenger.Reset()
|
||||
break
|
||||
} 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' {
|
||||
}
|
||||
if choice {
|
||||
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 {
|
||||
searchStart = view.Cursor.Loc
|
||||
if view.Cursor.HasSelection() {
|
||||
searchStart = ToCharPos(view.Cursor.CurSelection[1], view.Buf)
|
||||
} else {
|
||||
searchStart = ToCharPos(view.Cursor.Loc, view.Buf)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
} 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()
|
||||
|
||||
@@ -701,33 +596,93 @@ 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]
|
||||
args := SplitCommandArgs(input)[1:]
|
||||
|
||||
cmd := exec.Command(inputCmd, args...)
|
||||
outputBytes := &bytes.Buffer{}
|
||||
cmd.Stdout = outputBytes
|
||||
cmd.Stderr = outputBytes
|
||||
cmd.Start()
|
||||
err := cmd.Wait() // wait for command to finish
|
||||
outstring := outputBytes.String()
|
||||
return outstring, err
|
||||
}
|
||||
|
||||
// Term opens a terminal in the current view
|
||||
func Term(args []string) {
|
||||
var err error
|
||||
if len(args) == 0 {
|
||||
err = CurView().StartTerminal([]string{os.Getenv("SHELL"), "-i"}, true, false, "")
|
||||
// HandleShellCommand runs the shell command
|
||||
// The openTerm argument specifies whether a terminal should be opened (for viewing output
|
||||
// or interacting with stdin)
|
||||
func HandleShellCommand(input string, openTerm bool, waitToFinish bool) string {
|
||||
inputCmd := SplitCommandArgs(input)[0]
|
||||
if !openTerm {
|
||||
// Simply run the command in the background and notify the user when it's done
|
||||
messenger.Message("Running...")
|
||||
go func() {
|
||||
output, err := RunShellCommand(input)
|
||||
totalLines := strings.Split(output, "\n")
|
||||
|
||||
if len(totalLines) < 3 {
|
||||
if err == nil {
|
||||
messenger.Message(inputCmd, " exited without error")
|
||||
} else {
|
||||
messenger.Message(inputCmd, " exited with error: ", err, ": ", output)
|
||||
}
|
||||
} else {
|
||||
messenger.Message(output)
|
||||
}
|
||||
// We have to make sure to redraw
|
||||
RedrawAll()
|
||||
}()
|
||||
} else {
|
||||
err = CurView().StartTerminal(args, true, false, "")
|
||||
}
|
||||
if err != nil {
|
||||
messenger.Error(err)
|
||||
// Shut down the screen because we're going to interact directly with the shell
|
||||
screen.Fini()
|
||||
screen = nil
|
||||
|
||||
args := SplitCommandArgs(input)[1:]
|
||||
|
||||
// Set up everything for the command
|
||||
var outputBuf bytes.Buffer
|
||||
cmd := exec.Command(inputCmd, args...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = io.MultiWriter(os.Stdout, &outputBuf)
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
// This is a trap for Ctrl-C so that it doesn't kill micro
|
||||
// Instead we trap Ctrl-C to kill the program we're running
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
for range c {
|
||||
cmd.Process.Kill()
|
||||
}
|
||||
}()
|
||||
|
||||
cmd.Start()
|
||||
err := cmd.Wait()
|
||||
|
||||
output := outputBuf.String()
|
||||
if err != nil {
|
||||
output = err.Error()
|
||||
}
|
||||
|
||||
if waitToFinish {
|
||||
// This is just so we don't return right away and let the user press enter to return
|
||||
TermMessage("")
|
||||
}
|
||||
|
||||
// Start the screen back up
|
||||
InitScreen()
|
||||
|
||||
return output
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// HandleCommand handles input from the user
|
||||
func HandleCommand(input string) {
|
||||
args, err := shellwords.Split(input)
|
||||
if err != nil {
|
||||
messenger.Error("Error parsing args ", err)
|
||||
return
|
||||
}
|
||||
|
||||
args := SplitCommandArgs(input)
|
||||
inputCmd := args[0]
|
||||
|
||||
if _, ok := commands[inputCmd]; !ok {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
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.
|
||||
@@ -23,27 +21,15 @@ 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
|
||||
// Goto puts the cursor at the given cursor's location and gives the current cursor its selection too
|
||||
func (c *Cursor) Goto(b Cursor) {
|
||||
c.X, c.Y, c.LastVisualX = b.X, b.Y, b.LastVisualX
|
||||
c.OrigSelection, c.CurSelection = b.OrigSelection, b.CurSelection
|
||||
}
|
||||
|
||||
// GotoLoc puts the cursor at the given cursor's location and gives
|
||||
// the current cursor its selection too
|
||||
func (c *Cursor) GotoLoc(l Loc) {
|
||||
c.X, c.Y = l.X, l.Y
|
||||
c.LastVisualX = c.GetVisualX()
|
||||
}
|
||||
|
||||
// CopySelection copies the user's selection to either "primary"
|
||||
// or "clipboard"
|
||||
// CopySelection copies the user's selection to either "primary" or "clipboard"
|
||||
func (c *Cursor) CopySelection(target string) {
|
||||
if c.HasSelection() {
|
||||
if target != "primary" || c.buf.Settings["useprimary"].(bool) {
|
||||
@@ -160,8 +146,7 @@ func (c *Cursor) SelectWord() {
|
||||
c.Loc = c.CurSelection[1]
|
||||
}
|
||||
|
||||
// AddWordToSelection adds the word the cursor is currently on
|
||||
// to the selection
|
||||
// AddWordToSelection adds the word the cursor is currently on to the selection
|
||||
func (c *Cursor) AddWordToSelection() {
|
||||
if c.Loc.GreaterThan(c.OrigSelection[0]) && c.Loc.LessThan(c.OrigSelection[1]) {
|
||||
c.CurSelection = c.OrigSelection
|
||||
@@ -193,8 +178,7 @@ func (c *Cursor) AddWordToSelection() {
|
||||
c.Loc = c.CurSelection[1]
|
||||
}
|
||||
|
||||
// SelectTo selects from the current cursor location to the given
|
||||
// location
|
||||
// SelectTo selects from the current cursor location to the given location
|
||||
func (c *Cursor) SelectTo(loc Loc) {
|
||||
if loc.GreaterThan(c.OrigSelection[0]) {
|
||||
c.SetSelectionStart(c.OrigSelection[0])
|
||||
@@ -261,19 +245,19 @@ func (c *Cursor) UpN(amount int) {
|
||||
proposedY := c.Y - amount
|
||||
if proposedY < 0 {
|
||||
proposedY = 0
|
||||
c.LastVisualX = 0
|
||||
} else if proposedY >= c.buf.NumLines {
|
||||
proposedY = c.buf.NumLines - 1
|
||||
}
|
||||
|
||||
runes := []rune(c.buf.Line(c.Y))
|
||||
c.X = c.GetCharPosInLine(proposedY, c.LastVisualX)
|
||||
|
||||
if c.X > len(runes) || (amount < 0 && proposedY == c.Y) {
|
||||
c.X = len(runes)
|
||||
if proposedY == c.Y {
|
||||
return
|
||||
}
|
||||
|
||||
c.Y = proposedY
|
||||
runes := []rune(c.buf.Line(c.Y))
|
||||
c.X = c.GetCharPosInLine(c.Y, c.LastVisualX)
|
||||
if c.X > len(runes) {
|
||||
c.X = len(runes)
|
||||
}
|
||||
}
|
||||
|
||||
// DownN moves the cursor down N lines (if possible)
|
||||
@@ -291,8 +275,7 @@ func (c *Cursor) Down() {
|
||||
c.DownN(1)
|
||||
}
|
||||
|
||||
// Left moves the cursor left one cell (if possible) or to
|
||||
// the previous line if it is at the beginning
|
||||
// Left moves the cursor left one cell (if possible) or to the last line if it is at the beginning
|
||||
func (c *Cursor) Left() {
|
||||
if c.Loc == c.buf.Start() {
|
||||
return
|
||||
@@ -306,8 +289,7 @@ func (c *Cursor) Left() {
|
||||
c.LastVisualX = c.GetVisualX()
|
||||
}
|
||||
|
||||
// Right moves the cursor right one cell (if possible) or
|
||||
// to the next line if it is at the end
|
||||
// Right moves the cursor right one cell (if possible) or to the next line if it is at the end
|
||||
func (c *Cursor) Right() {
|
||||
if c.Loc == c.buf.End() {
|
||||
return
|
||||
@@ -333,9 +315,7 @@ func (c *Cursor) Start() {
|
||||
c.LastVisualX = c.GetVisualX()
|
||||
}
|
||||
|
||||
// GetCharPosInLine gets the char position of a visual x y
|
||||
// coordinate (this is necessary because tabs are 1 char but
|
||||
// 4 visual spaces)
|
||||
// GetCharPosInLine gets the char position of a visual x y coordinate (this is necessary because tabs are 1 char but 4 visual spaces)
|
||||
func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int {
|
||||
// Get the tab size
|
||||
tabSize := int(c.buf.Settings["tabsize"].(float64))
|
||||
@@ -354,25 +334,11 @@ 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
|
||||
// Relocate makes sure that the cursor is inside the bounds of the buffer
|
||||
// If it isn't, it moves it to be within the buffer's lines
|
||||
func (c *Cursor) Relocate() {
|
||||
if c.Y < 0 {
|
||||
c.Y = 0
|
||||
|
||||
@@ -28,7 +28,6 @@ type TextEvent struct {
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
// A Delta is a change to the buffer
|
||||
type Delta struct {
|
||||
Text string
|
||||
Start Loc
|
||||
@@ -49,11 +48,6 @@ func ExecuteTextEvent(t *TextEvent, buf *Buffer) {
|
||||
for i, d := range t.Deltas {
|
||||
t.Deltas[i].Text = buf.remove(d.Start, d.End)
|
||||
buf.insert(d.Start, []byte(d.Text))
|
||||
t.Deltas[i].Start = d.Start
|
||||
t.Deltas[i].End = Loc{d.Start.X + Count(d.Text), d.Start.Y}
|
||||
}
|
||||
for i, j := 0, len(t.Deltas)-1; i < j; i, j = i+1, j-1 {
|
||||
t.Deltas[i], t.Deltas[j] = t.Deltas[j], t.Deltas[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,65 +97,30 @@ 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.cursors[eh.buf.curCursor],
|
||||
C: eh.buf.Cursor,
|
||||
EventType: TextEventInsert,
|
||||
Deltas: []Delta{{text, start, Loc{0, 0}}},
|
||||
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.cursors[eh.buf.curCursor],
|
||||
C: eh.buf.Cursor,
|
||||
EventType: TextEventRemove,
|
||||
Deltas: []Delta{{"", start, end}},
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
// MultipleReplace creates an multiple insertions executes them
|
||||
func (eh *EventHandler) MultipleReplace(deltas []Delta) {
|
||||
e := &TextEvent{
|
||||
C: *eh.buf.cursors[eh.buf.curCursor],
|
||||
C: eh.buf.Cursor,
|
||||
EventType: TextEventReplace,
|
||||
Deltas: deltas,
|
||||
Time: time.Now(),
|
||||
@@ -236,12 +195,8 @@ func (eh *EventHandler) UndoOneEvent() {
|
||||
|
||||
// Set the cursor in the right place
|
||||
teCursor := t.C
|
||||
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
|
||||
}
|
||||
t.C = eh.buf.Cursor
|
||||
eh.buf.Cursor.Goto(teCursor)
|
||||
|
||||
// Push it to the redo stack
|
||||
eh.RedoStack.Push(t)
|
||||
@@ -283,12 +238,8 @@ func (eh *EventHandler) RedoOneEvent() {
|
||||
UndoTextEvent(t, eh.buf)
|
||||
|
||||
teCursor := t.C
|
||||
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
|
||||
}
|
||||
t.C = eh.buf.Cursor
|
||||
eh.buf.Cursor.Goto(teCursor)
|
||||
|
||||
eh.UndoStack.Push(t)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package highlight
|
||||
|
||||
import "regexp"
|
||||
|
||||
// MatchFiletype will use the list of syntax definitions provided and the filename and first line of the file
|
||||
// DetectFiletype will use the list of syntax definitions provided and the filename and first line of the file
|
||||
// to determine the filetype of the file
|
||||
// It will return the corresponding syntax definition for the filetype
|
||||
func MatchFiletype(ftdetect [2]*regexp.Regexp, filename string, firstLine []byte) bool {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package highlight
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
@@ -83,16 +82,6 @@ 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
|
||||
@@ -123,22 +112,14 @@ 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 r := recover(); r != nil {
|
||||
var ok bool
|
||||
err, ok = r.(error)
|
||||
if !ok {
|
||||
err = fmt.Errorf("pkg: %v", r)
|
||||
}
|
||||
if e := recover(); e != nil {
|
||||
err = e.(error)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -166,12 +147,8 @@ 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 r := recover(); r != nil {
|
||||
var ok bool
|
||||
err, ok = r.(error)
|
||||
if !ok {
|
||||
err = fmt.Errorf("pkg: %v", r)
|
||||
}
|
||||
if e := recover(); e != nil {
|
||||
err = e.(error)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -234,17 +211,8 @@ func resolveIncludesInRegion(files []*File, region *region) {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
func parseRules(input []interface{}, curRegion *region) (*rules, error) {
|
||||
rules := new(rules)
|
||||
|
||||
for _, v := range input {
|
||||
rule := v.(map[interface{}]interface{})
|
||||
@@ -254,7 +222,7 @@ func parseRules(input []interface{}, curRegion *region) (ru *rules, err error) {
|
||||
switch object := val.(type) {
|
||||
case string:
|
||||
if k == "include" {
|
||||
ru.includes = append(ru.includes, object)
|
||||
rules.includes = append(rules.includes, object)
|
||||
} else {
|
||||
// Pattern
|
||||
r, err := regexp.Compile(object)
|
||||
@@ -268,7 +236,7 @@ func parseRules(input []interface{}, curRegion *region) (ru *rules, err error) {
|
||||
Groups[groupStr] = numGroups
|
||||
}
|
||||
groupNum := Groups[groupStr]
|
||||
ru.patterns = append(ru.patterns, &pattern{groupNum, r})
|
||||
rules.patterns = append(rules.patterns, &pattern{groupNum, r})
|
||||
}
|
||||
case map[interface{}]interface{}:
|
||||
// region
|
||||
@@ -276,43 +244,35 @@ func parseRules(input []interface{}, curRegion *region) (ru *rules, err error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ru.regions = append(ru.regions, region)
|
||||
rules.regions = append(rules.regions, region)
|
||||
default:
|
||||
return nil, fmt.Errorf("Bad type %T", object)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ru, nil
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}()
|
||||
func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegion *region) (*region, error) {
|
||||
var err error
|
||||
|
||||
r = new(region)
|
||||
region := new(region)
|
||||
if _, ok := Groups[group]; !ok {
|
||||
numGroups++
|
||||
Groups[group] = numGroups
|
||||
}
|
||||
groupNum := Groups[group]
|
||||
r.group = groupNum
|
||||
r.parent = prevRegion
|
||||
region.group = groupNum
|
||||
region.parent = prevRegion
|
||||
|
||||
r.start, err = regexp.Compile(regionInfo["start"].(string))
|
||||
region.start, err = regexp.Compile(regionInfo["start"].(string))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.end, err = regexp.Compile(regionInfo["end"].(string))
|
||||
region.end, err = regexp.Compile(regionInfo["end"].(string))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -320,7 +280,7 @@ func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegio
|
||||
|
||||
// skip is optional
|
||||
if _, ok := regionInfo["skip"]; ok {
|
||||
r.skip, err = regexp.Compile(regionInfo["skip"].(string))
|
||||
region.skip, err = regexp.Compile(regionInfo["skip"].(string))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -335,20 +295,20 @@ func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegio
|
||||
Groups[groupStr] = numGroups
|
||||
}
|
||||
groupNum := Groups[groupStr]
|
||||
r.limitGroup = groupNum
|
||||
region.limitGroup = groupNum
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
r.limitGroup = r.group
|
||||
region.limitGroup = region.group
|
||||
}
|
||||
|
||||
r.rules, err = parseRules(regionInfo["rules"].([]interface{}), r)
|
||||
region.rules, err = parseRules(regionInfo["rules"].([]interface{}), region)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
return region, nil
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package main
|
||||
|
||||
// DisplayKeyMenu displays the nano-style key menu at the bottom of the screen
|
||||
func DisplayKeyMenu() {
|
||||
w, h := screen.Size()
|
||||
|
||||
bot := h - 3
|
||||
|
||||
display := []string{"^Q Quit, ^S Save, ^O Open, ^G Help, ^E Command Bar, ^K Cut Line", "^F Find, ^Z Undo, ^Y Redo, ^A Select All, ^D Duplicate Line, ^T New Tab"}
|
||||
|
||||
for y := 0; y < len(display); y++ {
|
||||
for x := 0; x < w; x++ {
|
||||
if x < len(display[y]) {
|
||||
screen.SetContent(x, bot+y, rune(display[y][x]), nil, defStyle)
|
||||
} else {
|
||||
screen.SetContent(x, bot+y, ' ', nil, defStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,6 @@ func runeToByteIndex(n int, txt []byte) int {
|
||||
return count
|
||||
}
|
||||
|
||||
// A Line contains the data in bytes as well as a highlight state, match
|
||||
// and a flag for whether the highlighting needs to be updated
|
||||
type Line struct {
|
||||
data []byte
|
||||
|
||||
@@ -45,12 +43,10 @@ type LineArray struct {
|
||||
lines []Line
|
||||
}
|
||||
|
||||
// Append efficiently appends lines together
|
||||
// It allocates an additional 10000 lines if the original estimate
|
||||
// is incorrect
|
||||
func Append(slice []Line, data ...Line) []Line {
|
||||
l := len(slice)
|
||||
if l+len(data) > cap(slice) { // reallocate
|
||||
// Allocate double what's needed, for future growth.
|
||||
newSlice := make([]Line, (l+len(data))+10000)
|
||||
// The copy function is predeclared and works for any slice type.
|
||||
copy(newSlice, slice)
|
||||
@@ -75,16 +71,6 @@ func NewLineArray(size int64, reader io.Reader) *LineArray {
|
||||
n := 0
|
||||
for {
|
||||
data, err := br.ReadBytes('\n')
|
||||
if len(data) > 1 && data[len(data)-2] == '\r' {
|
||||
data = append(data[:len(data)-2], '\n')
|
||||
if fileformat == 0 {
|
||||
fileformat = 2
|
||||
}
|
||||
} else if len(data) > 0 {
|
||||
if fileformat == 0 {
|
||||
fileformat = 1
|
||||
}
|
||||
}
|
||||
|
||||
if n >= 1000 && loaded >= 0 {
|
||||
totalLinesNum := int(float64(size) * (float64(n) / float64(loaded)))
|
||||
@@ -101,7 +87,7 @@ func NewLineArray(size int64, reader io.Reader) *LineArray {
|
||||
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
la.lines = Append(la.lines, Line{data[:], nil, nil, false})
|
||||
la.lines = Append(la.lines, Line{data[:len(data)], nil, nil, false})
|
||||
// la.lines = Append(la.lines, Line{data[:len(data)]})
|
||||
}
|
||||
// Last line was read
|
||||
@@ -128,23 +114,6 @@ func (la *LineArray) String() string {
|
||||
return str
|
||||
}
|
||||
|
||||
// SaveString returns the string that should be written to disk when
|
||||
// the line array is saved
|
||||
// It is the same as string but uses crlf or lf line endings depending
|
||||
func (la *LineArray) SaveString(useCrlf bool) string {
|
||||
str := ""
|
||||
for i, l := range la.lines {
|
||||
str += string(l.data)
|
||||
if i != len(la.lines)-1 {
|
||||
if useCrlf {
|
||||
str += "\r"
|
||||
}
|
||||
str += "\n"
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// NewlineBelow adds a newline below the given line number
|
||||
func (la *LineArray) NewlineBelow(y int) {
|
||||
la.lines = append(la.lines, Line{[]byte(" "), nil, nil, false})
|
||||
@@ -247,22 +216,18 @@ func (la *LineArray) Substr(start, end Loc) string {
|
||||
return str
|
||||
}
|
||||
|
||||
// State gets the highlight state for the given line number
|
||||
func (la *LineArray) State(lineN int) highlight.State {
|
||||
return la.lines[lineN].state
|
||||
}
|
||||
|
||||
// SetState sets the highlight state at the given line number
|
||||
func (la *LineArray) SetState(lineN int, s highlight.State) {
|
||||
la.lines[lineN].state = s
|
||||
}
|
||||
|
||||
// SetMatch sets the match at the given line number
|
||||
func (la *LineArray) SetMatch(lineN int, m highlight.LineMatch) {
|
||||
la.lines[lineN].match = m
|
||||
}
|
||||
|
||||
// Match retrieves the match for the given line number
|
||||
func (la *LineArray) Match(lineN int) highlight.LineMatch {
|
||||
return la.lines[lineN].match
|
||||
}
|
||||
|
||||
@@ -54,29 +54,6 @@ type Loc struct {
|
||||
X, Y int
|
||||
}
|
||||
|
||||
// Diff returns the distance between two locations
|
||||
func Diff(a, b Loc, buf *Buffer) int {
|
||||
if a.Y == b.Y {
|
||||
if a.X > b.X {
|
||||
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 {
|
||||
|
||||
539
cmd/micro/lua.go
539
cmd/micro/lua.go
@@ -1,539 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
luar "layeh.com/gopher-luar"
|
||||
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
)
|
||||
|
||||
var L *lua.LState
|
||||
|
||||
func init() {
|
||||
L = lua.NewState()
|
||||
L.SetGlobal("import", luar.New(L, Import))
|
||||
}
|
||||
|
||||
// LoadFile loads a lua file
|
||||
func LoadFile(module string, file string, data string) error {
|
||||
pluginDef := "local P = {};" + module + " = P;setmetatable(" + module + ", {__index = _G});setfenv(1, P);"
|
||||
|
||||
if fn, err := L.Load(strings.NewReader(pluginDef+data), file); err != nil {
|
||||
return err
|
||||
} else {
|
||||
L.Push(fn)
|
||||
return L.PCall(0, lua.MultRet, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Import allows a lua plugin to import a package
|
||||
func Import(pkg string) *lua.LTable {
|
||||
switch pkg {
|
||||
case "fmt":
|
||||
return importFmt()
|
||||
case "io":
|
||||
return importIo()
|
||||
case "io/ioutil", "ioutil":
|
||||
return importIoUtil()
|
||||
case "net":
|
||||
return importNet()
|
||||
case "math":
|
||||
return importMath()
|
||||
case "math/rand":
|
||||
return importMathRand()
|
||||
case "os":
|
||||
return importOs()
|
||||
case "runtime":
|
||||
return importRuntime()
|
||||
case "path":
|
||||
return importPath()
|
||||
case "filepath":
|
||||
return importFilePath()
|
||||
case "strings":
|
||||
return importStrings()
|
||||
case "regexp":
|
||||
return importRegexp()
|
||||
case "errors":
|
||||
return importErrors()
|
||||
case "time":
|
||||
return importTime()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func importFmt() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "tErrorf", luar.New(L, fmt.Errorf))
|
||||
L.SetField(pkg, "Fprint", luar.New(L, fmt.Fprint))
|
||||
L.SetField(pkg, "Fprintf", luar.New(L, fmt.Fprintf))
|
||||
L.SetField(pkg, "Fprintln", luar.New(L, fmt.Fprintln))
|
||||
L.SetField(pkg, "Fscan", luar.New(L, fmt.Fscan))
|
||||
L.SetField(pkg, "Fscanf", luar.New(L, fmt.Fscanf))
|
||||
L.SetField(pkg, "Fscanln", luar.New(L, fmt.Fscanln))
|
||||
L.SetField(pkg, "Print", luar.New(L, fmt.Print))
|
||||
L.SetField(pkg, "Printf", luar.New(L, fmt.Printf))
|
||||
L.SetField(pkg, "Println", luar.New(L, fmt.Println))
|
||||
L.SetField(pkg, "Scan", luar.New(L, fmt.Scan))
|
||||
L.SetField(pkg, "Scanf", luar.New(L, fmt.Scanf))
|
||||
L.SetField(pkg, "Scanln", luar.New(L, fmt.Scanln))
|
||||
L.SetField(pkg, "Sprint", luar.New(L, fmt.Sprint))
|
||||
L.SetField(pkg, "Sprintf", luar.New(L, fmt.Sprintf))
|
||||
L.SetField(pkg, "Sprintln", luar.New(L, fmt.Sprintln))
|
||||
L.SetField(pkg, "Sscan", luar.New(L, fmt.Sscan))
|
||||
L.SetField(pkg, "Sscanf", luar.New(L, fmt.Sscanf))
|
||||
L.SetField(pkg, "Sscanln", luar.New(L, fmt.Sscanln))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importIo() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "Copy", luar.New(L, io.Copy))
|
||||
L.SetField(pkg, "CopyN", luar.New(L, io.CopyN))
|
||||
L.SetField(pkg, "EOF", luar.New(L, io.EOF))
|
||||
L.SetField(pkg, "ErrClosedPipe", luar.New(L, io.ErrClosedPipe))
|
||||
L.SetField(pkg, "ErrNoProgress", luar.New(L, io.ErrNoProgress))
|
||||
L.SetField(pkg, "ErrShortBuffer", luar.New(L, io.ErrShortBuffer))
|
||||
L.SetField(pkg, "ErrShortWrite", luar.New(L, io.ErrShortWrite))
|
||||
L.SetField(pkg, "ErrUnexpectedEOF", luar.New(L, io.ErrUnexpectedEOF))
|
||||
L.SetField(pkg, "LimitReader", luar.New(L, io.LimitReader))
|
||||
L.SetField(pkg, "MultiReader", luar.New(L, io.MultiReader))
|
||||
L.SetField(pkg, "MultiWriter", luar.New(L, io.MultiWriter))
|
||||
L.SetField(pkg, "NewSectionReader", luar.New(L, io.NewSectionReader))
|
||||
L.SetField(pkg, "Pipe", luar.New(L, io.Pipe))
|
||||
L.SetField(pkg, "ReadAtLeast", luar.New(L, io.ReadAtLeast))
|
||||
L.SetField(pkg, "ReadFull", luar.New(L, io.ReadFull))
|
||||
L.SetField(pkg, "TeeReader", luar.New(L, io.TeeReader))
|
||||
L.SetField(pkg, "WriteString", luar.New(L, io.WriteString))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importIoUtil() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "ReadAll", luar.New(L, ioutil.ReadAll))
|
||||
L.SetField(pkg, "ReadDir", luar.New(L, ioutil.ReadDir))
|
||||
L.SetField(pkg, "ReadFile", luar.New(L, ioutil.ReadFile))
|
||||
L.SetField(pkg, "WriteFile", luar.New(L, ioutil.WriteFile))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importNet() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "CIDRMask", luar.New(L, net.CIDRMask))
|
||||
L.SetField(pkg, "Dial", luar.New(L, net.Dial))
|
||||
L.SetField(pkg, "DialIP", luar.New(L, net.DialIP))
|
||||
L.SetField(pkg, "DialTCP", luar.New(L, net.DialTCP))
|
||||
L.SetField(pkg, "DialTimeout", luar.New(L, net.DialTimeout))
|
||||
L.SetField(pkg, "DialUDP", luar.New(L, net.DialUDP))
|
||||
L.SetField(pkg, "DialUnix", luar.New(L, net.DialUnix))
|
||||
L.SetField(pkg, "ErrWriteToConnected", luar.New(L, net.ErrWriteToConnected))
|
||||
L.SetField(pkg, "FileConn", luar.New(L, net.FileConn))
|
||||
L.SetField(pkg, "FileListener", luar.New(L, net.FileListener))
|
||||
L.SetField(pkg, "FilePacketConn", luar.New(L, net.FilePacketConn))
|
||||
L.SetField(pkg, "FlagBroadcast", luar.New(L, net.FlagBroadcast))
|
||||
L.SetField(pkg, "FlagLoopback", luar.New(L, net.FlagLoopback))
|
||||
L.SetField(pkg, "FlagMulticast", luar.New(L, net.FlagMulticast))
|
||||
L.SetField(pkg, "FlagPointToPoint", luar.New(L, net.FlagPointToPoint))
|
||||
L.SetField(pkg, "FlagUp", luar.New(L, net.FlagUp))
|
||||
L.SetField(pkg, "IPv4", luar.New(L, net.IPv4))
|
||||
L.SetField(pkg, "IPv4Mask", luar.New(L, net.IPv4Mask))
|
||||
L.SetField(pkg, "IPv4allrouter", luar.New(L, net.IPv4allrouter))
|
||||
L.SetField(pkg, "IPv4allsys", luar.New(L, net.IPv4allsys))
|
||||
L.SetField(pkg, "IPv4bcast", luar.New(L, net.IPv4bcast))
|
||||
L.SetField(pkg, "IPv4len", luar.New(L, net.IPv4len))
|
||||
L.SetField(pkg, "IPv4zero", luar.New(L, net.IPv4zero))
|
||||
L.SetField(pkg, "IPv6interfacelocalallnodes", luar.New(L, net.IPv6interfacelocalallnodes))
|
||||
L.SetField(pkg, "IPv6len", luar.New(L, net.IPv6len))
|
||||
L.SetField(pkg, "IPv6linklocalallnodes", luar.New(L, net.IPv6linklocalallnodes))
|
||||
L.SetField(pkg, "IPv6linklocalallrouters", luar.New(L, net.IPv6linklocalallrouters))
|
||||
L.SetField(pkg, "IPv6loopback", luar.New(L, net.IPv6loopback))
|
||||
L.SetField(pkg, "IPv6unspecified", luar.New(L, net.IPv6unspecified))
|
||||
L.SetField(pkg, "IPv6zero", luar.New(L, net.IPv6zero))
|
||||
L.SetField(pkg, "InterfaceAddrs", luar.New(L, net.InterfaceAddrs))
|
||||
L.SetField(pkg, "InterfaceByIndex", luar.New(L, net.InterfaceByIndex))
|
||||
L.SetField(pkg, "InterfaceByName", luar.New(L, net.InterfaceByName))
|
||||
L.SetField(pkg, "Interfaces", luar.New(L, net.Interfaces))
|
||||
L.SetField(pkg, "JoinHostPort", luar.New(L, net.JoinHostPort))
|
||||
L.SetField(pkg, "Listen", luar.New(L, net.Listen))
|
||||
L.SetField(pkg, "ListenIP", luar.New(L, net.ListenIP))
|
||||
L.SetField(pkg, "ListenMulticastUDP", luar.New(L, net.ListenMulticastUDP))
|
||||
L.SetField(pkg, "ListenPacket", luar.New(L, net.ListenPacket))
|
||||
L.SetField(pkg, "ListenTCP", luar.New(L, net.ListenTCP))
|
||||
L.SetField(pkg, "ListenUDP", luar.New(L, net.ListenUDP))
|
||||
L.SetField(pkg, "ListenUnix", luar.New(L, net.ListenUnix))
|
||||
L.SetField(pkg, "ListenUnixgram", luar.New(L, net.ListenUnixgram))
|
||||
L.SetField(pkg, "LookupAddr", luar.New(L, net.LookupAddr))
|
||||
L.SetField(pkg, "LookupCNAME", luar.New(L, net.LookupCNAME))
|
||||
L.SetField(pkg, "LookupHost", luar.New(L, net.LookupHost))
|
||||
L.SetField(pkg, "LookupIP", luar.New(L, net.LookupIP))
|
||||
L.SetField(pkg, "LookupMX", luar.New(L, net.LookupMX))
|
||||
L.SetField(pkg, "LookupNS", luar.New(L, net.LookupNS))
|
||||
L.SetField(pkg, "LookupPort", luar.New(L, net.LookupPort))
|
||||
L.SetField(pkg, "LookupSRV", luar.New(L, net.LookupSRV))
|
||||
L.SetField(pkg, "LookupTXT", luar.New(L, net.LookupTXT))
|
||||
L.SetField(pkg, "ParseCIDR", luar.New(L, net.ParseCIDR))
|
||||
L.SetField(pkg, "ParseIP", luar.New(L, net.ParseIP))
|
||||
L.SetField(pkg, "ParseMAC", luar.New(L, net.ParseMAC))
|
||||
L.SetField(pkg, "Pipe", luar.New(L, net.Pipe))
|
||||
L.SetField(pkg, "ResolveIPAddr", luar.New(L, net.ResolveIPAddr))
|
||||
L.SetField(pkg, "ResolveTCPAddr", luar.New(L, net.ResolveTCPAddr))
|
||||
L.SetField(pkg, "ResolveUDPAddr", luar.New(L, net.ResolveUDPAddr))
|
||||
L.SetField(pkg, "ResolveUnixAddr", luar.New(L, net.ResolveUnixAddr))
|
||||
L.SetField(pkg, "SplitHostPort", luar.New(L, net.SplitHostPort))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importMath() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "Abs", luar.New(L, math.Abs))
|
||||
L.SetField(pkg, "Acos", luar.New(L, math.Acos))
|
||||
L.SetField(pkg, "Acosh", luar.New(L, math.Acosh))
|
||||
L.SetField(pkg, "Asin", luar.New(L, math.Asin))
|
||||
L.SetField(pkg, "Asinh", luar.New(L, math.Asinh))
|
||||
L.SetField(pkg, "Atan", luar.New(L, math.Atan))
|
||||
L.SetField(pkg, "Atan2", luar.New(L, math.Atan2))
|
||||
L.SetField(pkg, "Atanh", luar.New(L, math.Atanh))
|
||||
L.SetField(pkg, "Cbrt", luar.New(L, math.Cbrt))
|
||||
L.SetField(pkg, "Ceil", luar.New(L, math.Ceil))
|
||||
L.SetField(pkg, "Copysign", luar.New(L, math.Copysign))
|
||||
L.SetField(pkg, "Cos", luar.New(L, math.Cos))
|
||||
L.SetField(pkg, "Cosh", luar.New(L, math.Cosh))
|
||||
L.SetField(pkg, "Dim", luar.New(L, math.Dim))
|
||||
L.SetField(pkg, "Erf", luar.New(L, math.Erf))
|
||||
L.SetField(pkg, "Erfc", luar.New(L, math.Erfc))
|
||||
L.SetField(pkg, "Exp", luar.New(L, math.Exp))
|
||||
L.SetField(pkg, "Exp2", luar.New(L, math.Exp2))
|
||||
L.SetField(pkg, "Expm1", luar.New(L, math.Expm1))
|
||||
L.SetField(pkg, "Float32bits", luar.New(L, math.Float32bits))
|
||||
L.SetField(pkg, "Float32frombits", luar.New(L, math.Float32frombits))
|
||||
L.SetField(pkg, "Float64bits", luar.New(L, math.Float64bits))
|
||||
L.SetField(pkg, "Float64frombits", luar.New(L, math.Float64frombits))
|
||||
L.SetField(pkg, "Floor", luar.New(L, math.Floor))
|
||||
L.SetField(pkg, "Frexp", luar.New(L, math.Frexp))
|
||||
L.SetField(pkg, "Gamma", luar.New(L, math.Gamma))
|
||||
L.SetField(pkg, "Hypot", luar.New(L, math.Hypot))
|
||||
L.SetField(pkg, "Ilogb", luar.New(L, math.Ilogb))
|
||||
L.SetField(pkg, "Inf", luar.New(L, math.Inf))
|
||||
L.SetField(pkg, "IsInf", luar.New(L, math.IsInf))
|
||||
L.SetField(pkg, "IsNaN", luar.New(L, math.IsNaN))
|
||||
L.SetField(pkg, "J0", luar.New(L, math.J0))
|
||||
L.SetField(pkg, "J1", luar.New(L, math.J1))
|
||||
L.SetField(pkg, "Jn", luar.New(L, math.Jn))
|
||||
L.SetField(pkg, "Ldexp", luar.New(L, math.Ldexp))
|
||||
L.SetField(pkg, "Lgamma", luar.New(L, math.Lgamma))
|
||||
L.SetField(pkg, "Log", luar.New(L, math.Log))
|
||||
L.SetField(pkg, "Log10", luar.New(L, math.Log10))
|
||||
L.SetField(pkg, "Log1p", luar.New(L, math.Log1p))
|
||||
L.SetField(pkg, "Log2", luar.New(L, math.Log2))
|
||||
L.SetField(pkg, "Logb", luar.New(L, math.Logb))
|
||||
L.SetField(pkg, "Max", luar.New(L, math.Max))
|
||||
L.SetField(pkg, "Min", luar.New(L, math.Min))
|
||||
L.SetField(pkg, "Mod", luar.New(L, math.Mod))
|
||||
L.SetField(pkg, "Modf", luar.New(L, math.Modf))
|
||||
L.SetField(pkg, "NaN", luar.New(L, math.NaN))
|
||||
L.SetField(pkg, "Nextafter", luar.New(L, math.Nextafter))
|
||||
L.SetField(pkg, "Pow", luar.New(L, math.Pow))
|
||||
L.SetField(pkg, "Pow10", luar.New(L, math.Pow10))
|
||||
L.SetField(pkg, "Remainder", luar.New(L, math.Remainder))
|
||||
L.SetField(pkg, "Signbit", luar.New(L, math.Signbit))
|
||||
L.SetField(pkg, "Sin", luar.New(L, math.Sin))
|
||||
L.SetField(pkg, "Sincos", luar.New(L, math.Sincos))
|
||||
L.SetField(pkg, "Sinh", luar.New(L, math.Sinh))
|
||||
L.SetField(pkg, "Sqrt", luar.New(L, math.Sqrt))
|
||||
L.SetField(pkg, "Tan", luar.New(L, math.Tan))
|
||||
L.SetField(pkg, "Tanh", luar.New(L, math.Tanh))
|
||||
L.SetField(pkg, "Trunc", luar.New(L, math.Trunc))
|
||||
L.SetField(pkg, "Y0", luar.New(L, math.Y0))
|
||||
L.SetField(pkg, "Y1", luar.New(L, math.Y1))
|
||||
L.SetField(pkg, "Yn", luar.New(L, math.Yn))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importMathRand() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "ExpFloat64", luar.New(L, rand.ExpFloat64))
|
||||
L.SetField(pkg, "Float32", luar.New(L, rand.Float32))
|
||||
L.SetField(pkg, "Float64", luar.New(L, rand.Float64))
|
||||
L.SetField(pkg, "Int", luar.New(L, rand.Int))
|
||||
L.SetField(pkg, "Int31", luar.New(L, rand.Int31))
|
||||
L.SetField(pkg, "Int31n", luar.New(L, rand.Int31n))
|
||||
L.SetField(pkg, "Int63", luar.New(L, rand.Int63))
|
||||
L.SetField(pkg, "Int63n", luar.New(L, rand.Int63n))
|
||||
L.SetField(pkg, "Intn", luar.New(L, rand.Intn))
|
||||
L.SetField(pkg, "NormFloat64", luar.New(L, rand.NormFloat64))
|
||||
L.SetField(pkg, "Perm", luar.New(L, rand.Perm))
|
||||
L.SetField(pkg, "Seed", luar.New(L, rand.Seed))
|
||||
L.SetField(pkg, "Uint32", luar.New(L, rand.Uint32))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importOs() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "Args", luar.New(L, os.Args))
|
||||
L.SetField(pkg, "Chdir", luar.New(L, os.Chdir))
|
||||
L.SetField(pkg, "Chmod", luar.New(L, os.Chmod))
|
||||
L.SetField(pkg, "Chown", luar.New(L, os.Chown))
|
||||
L.SetField(pkg, "Chtimes", luar.New(L, os.Chtimes))
|
||||
L.SetField(pkg, "Clearenv", luar.New(L, os.Clearenv))
|
||||
L.SetField(pkg, "Create", luar.New(L, os.Create))
|
||||
L.SetField(pkg, "DevNull", luar.New(L, os.DevNull))
|
||||
L.SetField(pkg, "Environ", luar.New(L, os.Environ))
|
||||
L.SetField(pkg, "ErrExist", luar.New(L, os.ErrExist))
|
||||
L.SetField(pkg, "ErrInvalid", luar.New(L, os.ErrInvalid))
|
||||
L.SetField(pkg, "ErrNotExist", luar.New(L, os.ErrNotExist))
|
||||
L.SetField(pkg, "ErrPermission", luar.New(L, os.ErrPermission))
|
||||
L.SetField(pkg, "Exit", luar.New(L, os.Exit))
|
||||
L.SetField(pkg, "Expand", luar.New(L, os.Expand))
|
||||
L.SetField(pkg, "ExpandEnv", luar.New(L, os.ExpandEnv))
|
||||
L.SetField(pkg, "FindProcess", luar.New(L, os.FindProcess))
|
||||
L.SetField(pkg, "Getegid", luar.New(L, os.Getegid))
|
||||
L.SetField(pkg, "Getenv", luar.New(L, os.Getenv))
|
||||
L.SetField(pkg, "Geteuid", luar.New(L, os.Geteuid))
|
||||
L.SetField(pkg, "Getgid", luar.New(L, os.Getgid))
|
||||
L.SetField(pkg, "Getgroups", luar.New(L, os.Getgroups))
|
||||
L.SetField(pkg, "Getpagesize", luar.New(L, os.Getpagesize))
|
||||
L.SetField(pkg, "Getpid", luar.New(L, os.Getpid))
|
||||
L.SetField(pkg, "Getuid", luar.New(L, os.Getuid))
|
||||
L.SetField(pkg, "Getwd", luar.New(L, os.Getwd))
|
||||
L.SetField(pkg, "Hostname", luar.New(L, os.Hostname))
|
||||
L.SetField(pkg, "Interrupt", luar.New(L, os.Interrupt))
|
||||
L.SetField(pkg, "IsExist", luar.New(L, os.IsExist))
|
||||
L.SetField(pkg, "IsNotExist", luar.New(L, os.IsNotExist))
|
||||
L.SetField(pkg, "IsPathSeparator", luar.New(L, os.IsPathSeparator))
|
||||
L.SetField(pkg, "IsPermission", luar.New(L, os.IsPermission))
|
||||
L.SetField(pkg, "Kill", luar.New(L, os.Kill))
|
||||
L.SetField(pkg, "Lchown", luar.New(L, os.Lchown))
|
||||
L.SetField(pkg, "Link", luar.New(L, os.Link))
|
||||
L.SetField(pkg, "Lstat", luar.New(L, os.Lstat))
|
||||
L.SetField(pkg, "Mkdir", luar.New(L, os.Mkdir))
|
||||
L.SetField(pkg, "MkdirAll", luar.New(L, os.MkdirAll))
|
||||
L.SetField(pkg, "ModeAppend", luar.New(L, os.ModeAppend))
|
||||
L.SetField(pkg, "ModeCharDevice", luar.New(L, os.ModeCharDevice))
|
||||
L.SetField(pkg, "ModeDevice", luar.New(L, os.ModeDevice))
|
||||
L.SetField(pkg, "ModeDir", luar.New(L, os.ModeDir))
|
||||
L.SetField(pkg, "ModeExclusive", luar.New(L, os.ModeExclusive))
|
||||
L.SetField(pkg, "ModeNamedPipe", luar.New(L, os.ModeNamedPipe))
|
||||
L.SetField(pkg, "ModePerm", luar.New(L, os.ModePerm))
|
||||
L.SetField(pkg, "ModeSetgid", luar.New(L, os.ModeSetgid))
|
||||
L.SetField(pkg, "ModeSetuid", luar.New(L, os.ModeSetuid))
|
||||
L.SetField(pkg, "ModeSocket", luar.New(L, os.ModeSocket))
|
||||
L.SetField(pkg, "ModeSticky", luar.New(L, os.ModeSticky))
|
||||
L.SetField(pkg, "ModeSymlink", luar.New(L, os.ModeSymlink))
|
||||
L.SetField(pkg, "ModeTemporary", luar.New(L, os.ModeTemporary))
|
||||
L.SetField(pkg, "ModeType", luar.New(L, os.ModeType))
|
||||
L.SetField(pkg, "NewFile", luar.New(L, os.NewFile))
|
||||
L.SetField(pkg, "NewSyscallError", luar.New(L, os.NewSyscallError))
|
||||
L.SetField(pkg, "O_APPEND", luar.New(L, os.O_APPEND))
|
||||
L.SetField(pkg, "O_CREATE", luar.New(L, os.O_CREATE))
|
||||
L.SetField(pkg, "O_EXCL", luar.New(L, os.O_EXCL))
|
||||
L.SetField(pkg, "O_RDONLY", luar.New(L, os.O_RDONLY))
|
||||
L.SetField(pkg, "O_RDWR", luar.New(L, os.O_RDWR))
|
||||
L.SetField(pkg, "O_SYNC", luar.New(L, os.O_SYNC))
|
||||
L.SetField(pkg, "O_TRUNC", luar.New(L, os.O_TRUNC))
|
||||
L.SetField(pkg, "O_WRONLY", luar.New(L, os.O_WRONLY))
|
||||
L.SetField(pkg, "Open", luar.New(L, os.Open))
|
||||
L.SetField(pkg, "OpenFile", luar.New(L, os.OpenFile))
|
||||
L.SetField(pkg, "PathListSeparator", luar.New(L, os.PathListSeparator))
|
||||
L.SetField(pkg, "PathSeparator", luar.New(L, os.PathSeparator))
|
||||
L.SetField(pkg, "Pipe", luar.New(L, os.Pipe))
|
||||
L.SetField(pkg, "Readlink", luar.New(L, os.Readlink))
|
||||
L.SetField(pkg, "Remove", luar.New(L, os.Remove))
|
||||
L.SetField(pkg, "RemoveAll", luar.New(L, os.RemoveAll))
|
||||
L.SetField(pkg, "Rename", luar.New(L, os.Rename))
|
||||
L.SetField(pkg, "SEEK_CUR", luar.New(L, os.SEEK_CUR))
|
||||
L.SetField(pkg, "SEEK_END", luar.New(L, os.SEEK_END))
|
||||
L.SetField(pkg, "SEEK_SET", luar.New(L, os.SEEK_SET))
|
||||
L.SetField(pkg, "SameFile", luar.New(L, os.SameFile))
|
||||
L.SetField(pkg, "Setenv", luar.New(L, os.Setenv))
|
||||
L.SetField(pkg, "StartProcess", luar.New(L, os.StartProcess))
|
||||
L.SetField(pkg, "Stat", luar.New(L, os.Stat))
|
||||
L.SetField(pkg, "Stderr", luar.New(L, os.Stderr))
|
||||
L.SetField(pkg, "Stdin", luar.New(L, os.Stdin))
|
||||
L.SetField(pkg, "Stdout", luar.New(L, os.Stdout))
|
||||
L.SetField(pkg, "Symlink", luar.New(L, os.Symlink))
|
||||
L.SetField(pkg, "TempDir", luar.New(L, os.TempDir))
|
||||
L.SetField(pkg, "Truncate", luar.New(L, os.Truncate))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importRuntime() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "GC", luar.New(L, runtime.GC))
|
||||
L.SetField(pkg, "GOARCH", luar.New(L, runtime.GOARCH))
|
||||
L.SetField(pkg, "GOMAXPROCS", luar.New(L, runtime.GOMAXPROCS))
|
||||
L.SetField(pkg, "GOOS", luar.New(L, runtime.GOOS))
|
||||
L.SetField(pkg, "GOROOT", luar.New(L, runtime.GOROOT))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importPath() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "Base", luar.New(L, path.Base))
|
||||
L.SetField(pkg, "Clean", luar.New(L, path.Clean))
|
||||
L.SetField(pkg, "Dir", luar.New(L, path.Dir))
|
||||
L.SetField(pkg, "ErrBadPattern", luar.New(L, path.ErrBadPattern))
|
||||
L.SetField(pkg, "Ext", luar.New(L, path.Ext))
|
||||
L.SetField(pkg, "IsAbs", luar.New(L, path.IsAbs))
|
||||
L.SetField(pkg, "Join", luar.New(L, path.Join))
|
||||
L.SetField(pkg, "Match", luar.New(L, path.Match))
|
||||
L.SetField(pkg, "Split", luar.New(L, path.Split))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importFilePath() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "Join", luar.New(L, filepath.Join))
|
||||
L.SetField(pkg, "Abs", luar.New(L, filepath.Abs))
|
||||
L.SetField(pkg, "Base", luar.New(L, filepath.Base))
|
||||
L.SetField(pkg, "Clean", luar.New(L, filepath.Clean))
|
||||
L.SetField(pkg, "Dir", luar.New(L, filepath.Dir))
|
||||
L.SetField(pkg, "EvalSymlinks", luar.New(L, filepath.EvalSymlinks))
|
||||
L.SetField(pkg, "Ext", luar.New(L, filepath.Ext))
|
||||
L.SetField(pkg, "FromSlash", luar.New(L, filepath.FromSlash))
|
||||
L.SetField(pkg, "Glob", luar.New(L, filepath.Glob))
|
||||
L.SetField(pkg, "HasPrefix", luar.New(L, filepath.HasPrefix))
|
||||
L.SetField(pkg, "IsAbs", luar.New(L, filepath.IsAbs))
|
||||
L.SetField(pkg, "Join", luar.New(L, filepath.Join))
|
||||
L.SetField(pkg, "Match", luar.New(L, filepath.Match))
|
||||
L.SetField(pkg, "Rel", luar.New(L, filepath.Rel))
|
||||
L.SetField(pkg, "Split", luar.New(L, filepath.Split))
|
||||
L.SetField(pkg, "SplitList", luar.New(L, filepath.SplitList))
|
||||
L.SetField(pkg, "ToSlash", luar.New(L, filepath.ToSlash))
|
||||
L.SetField(pkg, "VolumeName", luar.New(L, filepath.VolumeName))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importStrings() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "Contains", luar.New(L, strings.Contains))
|
||||
L.SetField(pkg, "ContainsAny", luar.New(L, strings.ContainsAny))
|
||||
L.SetField(pkg, "ContainsRune", luar.New(L, strings.ContainsRune))
|
||||
L.SetField(pkg, "Count", luar.New(L, strings.Count))
|
||||
L.SetField(pkg, "EqualFold", luar.New(L, strings.EqualFold))
|
||||
L.SetField(pkg, "Fields", luar.New(L, strings.Fields))
|
||||
L.SetField(pkg, "FieldsFunc", luar.New(L, strings.FieldsFunc))
|
||||
L.SetField(pkg, "HasPrefix", luar.New(L, strings.HasPrefix))
|
||||
L.SetField(pkg, "HasSuffix", luar.New(L, strings.HasSuffix))
|
||||
L.SetField(pkg, "Index", luar.New(L, strings.Index))
|
||||
L.SetField(pkg, "IndexAny", luar.New(L, strings.IndexAny))
|
||||
L.SetField(pkg, "IndexByte", luar.New(L, strings.IndexByte))
|
||||
L.SetField(pkg, "IndexFunc", luar.New(L, strings.IndexFunc))
|
||||
L.SetField(pkg, "IndexRune", luar.New(L, strings.IndexRune))
|
||||
L.SetField(pkg, "Join", luar.New(L, strings.Join))
|
||||
L.SetField(pkg, "LastIndex", luar.New(L, strings.LastIndex))
|
||||
L.SetField(pkg, "LastIndexAny", luar.New(L, strings.LastIndexAny))
|
||||
L.SetField(pkg, "LastIndexFunc", luar.New(L, strings.LastIndexFunc))
|
||||
L.SetField(pkg, "Map", luar.New(L, strings.Map))
|
||||
L.SetField(pkg, "NewReader", luar.New(L, strings.NewReader))
|
||||
L.SetField(pkg, "NewReplacer", luar.New(L, strings.NewReplacer))
|
||||
L.SetField(pkg, "Repeat", luar.New(L, strings.Repeat))
|
||||
L.SetField(pkg, "Replace", luar.New(L, strings.Replace))
|
||||
L.SetField(pkg, "Split", luar.New(L, strings.Split))
|
||||
L.SetField(pkg, "SplitAfter", luar.New(L, strings.SplitAfter))
|
||||
L.SetField(pkg, "SplitAfterN", luar.New(L, strings.SplitAfterN))
|
||||
L.SetField(pkg, "SplitN", luar.New(L, strings.SplitN))
|
||||
L.SetField(pkg, "Title", luar.New(L, strings.Title))
|
||||
L.SetField(pkg, "ToLower", luar.New(L, strings.ToLower))
|
||||
L.SetField(pkg, "ToLowerSpecial", luar.New(L, strings.ToLowerSpecial))
|
||||
L.SetField(pkg, "ToTitle", luar.New(L, strings.ToTitle))
|
||||
L.SetField(pkg, "ToTitleSpecial", luar.New(L, strings.ToTitleSpecial))
|
||||
L.SetField(pkg, "ToUpper", luar.New(L, strings.ToUpper))
|
||||
L.SetField(pkg, "ToUpperSpecial", luar.New(L, strings.ToUpperSpecial))
|
||||
L.SetField(pkg, "Trim", luar.New(L, strings.Trim))
|
||||
L.SetField(pkg, "TrimFunc", luar.New(L, strings.TrimFunc))
|
||||
L.SetField(pkg, "TrimLeft", luar.New(L, strings.TrimLeft))
|
||||
L.SetField(pkg, "TrimLeftFunc", luar.New(L, strings.TrimLeftFunc))
|
||||
L.SetField(pkg, "TrimPrefix", luar.New(L, strings.TrimPrefix))
|
||||
L.SetField(pkg, "TrimRight", luar.New(L, strings.TrimRight))
|
||||
L.SetField(pkg, "TrimRightFunc", luar.New(L, strings.TrimRightFunc))
|
||||
L.SetField(pkg, "TrimSpace", luar.New(L, strings.TrimSpace))
|
||||
L.SetField(pkg, "TrimSuffix", luar.New(L, strings.TrimSuffix))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importRegexp() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "Match", luar.New(L, regexp.Match))
|
||||
L.SetField(pkg, "MatchReader", luar.New(L, regexp.MatchReader))
|
||||
L.SetField(pkg, "MatchString", luar.New(L, regexp.MatchString))
|
||||
L.SetField(pkg, "QuoteMeta", luar.New(L, regexp.QuoteMeta))
|
||||
L.SetField(pkg, "Compile", luar.New(L, regexp.Compile))
|
||||
L.SetField(pkg, "CompilePOSIX", luar.New(L, regexp.CompilePOSIX))
|
||||
L.SetField(pkg, "MustCompile", luar.New(L, regexp.MustCompile))
|
||||
L.SetField(pkg, "MustCompilePOSIX", luar.New(L, regexp.MustCompilePOSIX))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importErrors() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "New", luar.New(L, errors.New))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importTime() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "After", luar.New(L, time.After))
|
||||
L.SetField(pkg, "Sleep", luar.New(L, time.Sleep))
|
||||
L.SetField(pkg, "Tick", luar.New(L, time.Tick))
|
||||
L.SetField(pkg, "Since", luar.New(L, time.Since))
|
||||
L.SetField(pkg, "FixedZone", luar.New(L, time.FixedZone))
|
||||
L.SetField(pkg, "LoadLocation", luar.New(L, time.LoadLocation))
|
||||
L.SetField(pkg, "NewTicker", luar.New(L, time.NewTicker))
|
||||
L.SetField(pkg, "Date", luar.New(L, time.Date))
|
||||
L.SetField(pkg, "Now", luar.New(L, time.Now))
|
||||
L.SetField(pkg, "Parse", luar.New(L, time.Parse))
|
||||
L.SetField(pkg, "ParseDuration", luar.New(L, time.ParseDuration))
|
||||
L.SetField(pkg, "ParseInLocation", luar.New(L, time.ParseInLocation))
|
||||
L.SetField(pkg, "Unix", luar.New(L, time.Unix))
|
||||
L.SetField(pkg, "AfterFunc", luar.New(L, time.AfterFunc))
|
||||
L.SetField(pkg, "NewTimer", luar.New(L, time.NewTimer))
|
||||
L.SetField(pkg, "Nanosecond", luar.New(L, time.Nanosecond))
|
||||
L.SetField(pkg, "Microsecond", luar.New(L, time.Microsecond))
|
||||
L.SetField(pkg, "Millisecond", luar.New(L, time.Millisecond))
|
||||
L.SetField(pkg, "Second", luar.New(L, time.Second))
|
||||
L.SetField(pkg, "Minute", luar.New(L, time.Minute))
|
||||
L.SetField(pkg, "Hour", luar.New(L, time.Hour))
|
||||
|
||||
return pkg
|
||||
}
|
||||
@@ -3,14 +3,11 @@ package main
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/zyedidia/clipboard"
|
||||
"github.com/zyedidia/micro/cmd/micro/shellwords"
|
||||
"github.com/zyedidia/tcell"
|
||||
)
|
||||
|
||||
@@ -72,11 +69,9 @@ type Messenger struct {
|
||||
gutterMessage bool
|
||||
}
|
||||
|
||||
// AddLog sends a message to the log view
|
||||
func (m *Messenger) AddLog(msg ...interface{}) {
|
||||
logMessage := fmt.Sprint(msg...)
|
||||
func (m *Messenger) AddLog(msg string) {
|
||||
buffer := m.getBuffer()
|
||||
buffer.insert(buffer.End(), []byte(logMessage+"\n"))
|
||||
buffer.insert(buffer.End(), []byte(msg+"\n"))
|
||||
buffer.Cursor.Loc = buffer.End()
|
||||
buffer.Cursor.Relocate()
|
||||
}
|
||||
@@ -223,7 +218,6 @@ func (m *Messenger) LetterPrompt(prompt string, responses ...rune) (rune, bool)
|
||||
}
|
||||
}
|
||||
|
||||
// Completion represents a type of completion
|
||||
type Completion int
|
||||
|
||||
const (
|
||||
@@ -234,7 +228,6 @@ const (
|
||||
OptionCompletion
|
||||
PluginCmdCompletion
|
||||
PluginNameCompletion
|
||||
OptionValueCompletion
|
||||
)
|
||||
|
||||
// Prompt sends the user a message and waits for a response to be typed in
|
||||
@@ -274,16 +267,9 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
|
||||
response, canceled = m.response, false
|
||||
m.history[historyType][len(m.history[historyType])-1] = response
|
||||
case tcell.KeyTab:
|
||||
args, err := shellwords.Split(m.response)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
currentArg := ""
|
||||
currentArgNum := 0
|
||||
if len(args) > 0 {
|
||||
currentArgNum = len(args) - 1
|
||||
currentArg = args[currentArgNum]
|
||||
}
|
||||
args := SplitCommandArgs(m.response)
|
||||
currentArgNum := len(args) - 1
|
||||
currentArg := args[currentArgNum]
|
||||
var completionType Completion
|
||||
|
||||
if completionTypes[0] == CommandCompletion && currentArgNum > 0 {
|
||||
@@ -307,10 +293,6 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
|
||||
chosen, suggestions = HelpComplete(currentArg)
|
||||
} else if completionType == OptionCompletion {
|
||||
chosen, suggestions = OptionComplete(currentArg)
|
||||
} else if completionType == OptionValueCompletion {
|
||||
if currentArgNum-1 > 0 {
|
||||
chosen, suggestions = OptionValueComplete(args[currentArgNum-1], currentArg)
|
||||
}
|
||||
} else if completionType == PluginCmdCompletion {
|
||||
chosen, suggestions = PluginCmdComplete(currentArg)
|
||||
} else if completionType == PluginNameCompletion {
|
||||
@@ -323,8 +305,8 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
|
||||
chosen = chosen + CommonSubstring(suggestions...)
|
||||
}
|
||||
|
||||
if len(suggestions) != 0 && chosen != "" {
|
||||
m.response = shellwords.Join(append(args[:len(args)-1], chosen)...)
|
||||
if chosen != "" {
|
||||
m.response = JoinCommandArgs(append(args[:len(args)-1], chosen)...)
|
||||
m.cursorx = Count(m.response)
|
||||
}
|
||||
}
|
||||
@@ -349,160 +331,62 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
|
||||
return response, canceled
|
||||
}
|
||||
|
||||
// UpHistory fetches the previous item in the history
|
||||
func (m *Messenger) UpHistory(history []string) {
|
||||
if m.historyNum > 0 {
|
||||
m.historyNum--
|
||||
m.response = history[m.historyNum]
|
||||
m.cursorx = Count(m.response)
|
||||
}
|
||||
}
|
||||
|
||||
// DownHistory fetches the next item in the history
|
||||
func (m *Messenger) DownHistory(history []string) {
|
||||
if m.historyNum < len(history)-1 {
|
||||
m.historyNum++
|
||||
m.response = history[m.historyNum]
|
||||
m.cursorx = Count(m.response)
|
||||
}
|
||||
}
|
||||
|
||||
// CursorLeft moves the cursor one character left
|
||||
func (m *Messenger) CursorLeft() {
|
||||
if m.cursorx > 0 {
|
||||
m.cursorx--
|
||||
}
|
||||
}
|
||||
|
||||
// CursorRight moves the cursor one character right
|
||||
func (m *Messenger) CursorRight() {
|
||||
if m.cursorx < Count(m.response) {
|
||||
m.cursorx++
|
||||
}
|
||||
}
|
||||
|
||||
// Start moves the cursor to the start of the line
|
||||
func (m *Messenger) Start() {
|
||||
m.cursorx = 0
|
||||
}
|
||||
|
||||
// End moves the cursor to the end of the line
|
||||
func (m *Messenger) End() {
|
||||
m.cursorx = Count(m.response)
|
||||
}
|
||||
|
||||
// Backspace deletes one character
|
||||
func (m *Messenger) Backspace() {
|
||||
if m.cursorx > 0 {
|
||||
m.response = string([]rune(m.response)[:m.cursorx-1]) + string([]rune(m.response)[m.cursorx:])
|
||||
m.cursorx--
|
||||
}
|
||||
}
|
||||
|
||||
// Paste pastes the clipboard
|
||||
func (m *Messenger) Paste() {
|
||||
clip, _ := clipboard.ReadAll("clipboard")
|
||||
m.response = Insert(m.response, m.cursorx, clip)
|
||||
m.cursorx += Count(clip)
|
||||
}
|
||||
|
||||
// WordLeft moves the cursor one word to the left
|
||||
func (m *Messenger) WordLeft() {
|
||||
response := []rune(m.response)
|
||||
m.CursorLeft()
|
||||
if m.cursorx <= 0 {
|
||||
return
|
||||
}
|
||||
for IsWhitespace(response[m.cursorx]) {
|
||||
if m.cursorx <= 0 {
|
||||
return
|
||||
}
|
||||
m.CursorLeft()
|
||||
}
|
||||
m.CursorLeft()
|
||||
for IsWordChar(string(response[m.cursorx])) {
|
||||
if m.cursorx <= 0 {
|
||||
return
|
||||
}
|
||||
m.CursorLeft()
|
||||
}
|
||||
m.CursorRight()
|
||||
}
|
||||
|
||||
// WordRight moves the cursor one word to the right
|
||||
func (m *Messenger) WordRight() {
|
||||
response := []rune(m.response)
|
||||
if m.cursorx >= len(response) {
|
||||
return
|
||||
}
|
||||
for IsWhitespace(response[m.cursorx]) {
|
||||
m.CursorRight()
|
||||
if m.cursorx >= len(response) {
|
||||
m.CursorRight()
|
||||
return
|
||||
}
|
||||
}
|
||||
m.CursorRight()
|
||||
if m.cursorx >= len(response) {
|
||||
return
|
||||
}
|
||||
for IsWordChar(string(response[m.cursorx])) {
|
||||
m.CursorRight()
|
||||
if m.cursorx >= len(response) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteWordLeft deletes one word to the left
|
||||
func (m *Messenger) DeleteWordLeft() {
|
||||
m.WordLeft()
|
||||
m.response = string([]rune(m.response)[:m.cursorx])
|
||||
}
|
||||
|
||||
// HandleEvent handles an event for the prompter
|
||||
func (m *Messenger) HandleEvent(event tcell.Event, history []string) {
|
||||
switch e := event.(type) {
|
||||
case *tcell.EventKey:
|
||||
if e.Key() != tcell.KeyRune || e.Modifiers() != 0 {
|
||||
for key, actions := range bindings {
|
||||
if e.Key() == key.keyCode {
|
||||
if e.Key() == tcell.KeyRune {
|
||||
if e.Rune() != key.r {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if e.Modifiers() == key.modifiers {
|
||||
for _, action := range actions {
|
||||
funcName := FuncName(action)
|
||||
switch funcName {
|
||||
case "main.(*View).CursorUp":
|
||||
if m.historyNum > 0 {
|
||||
m.historyNum--
|
||||
m.response = history[m.historyNum]
|
||||
m.cursorx = Count(m.response)
|
||||
}
|
||||
case "main.(*View).CursorDown":
|
||||
if m.historyNum < len(history)-1 {
|
||||
m.historyNum++
|
||||
m.response = history[m.historyNum]
|
||||
m.cursorx = Count(m.response)
|
||||
}
|
||||
case "main.(*View).CursorLeft":
|
||||
if m.cursorx > 0 {
|
||||
m.cursorx--
|
||||
}
|
||||
case "main.(*View).CursorRight":
|
||||
if m.cursorx < Count(m.response) {
|
||||
m.cursorx++
|
||||
}
|
||||
case "main.(*View).CursorStart", "main.(*View).StartOfLine":
|
||||
m.cursorx = 0
|
||||
case "main.(*View).CursorEnd", "main.(*View).EndOfLine":
|
||||
m.cursorx = Count(m.response)
|
||||
case "main.(*View).Backspace":
|
||||
if m.cursorx > 0 {
|
||||
m.response = string([]rune(m.response)[:m.cursorx-1]) + string([]rune(m.response)[m.cursorx:])
|
||||
m.cursorx--
|
||||
}
|
||||
case "main.(*View).Paste":
|
||||
clip, _ := clipboard.ReadAll("clipboard")
|
||||
m.response = Insert(m.response, m.cursorx, clip)
|
||||
m.cursorx += Count(clip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
switch e.Key() {
|
||||
case tcell.KeyCtrlA:
|
||||
m.Start()
|
||||
case tcell.KeyCtrlE:
|
||||
m.End()
|
||||
case tcell.KeyUp:
|
||||
m.UpHistory(history)
|
||||
case tcell.KeyDown:
|
||||
m.DownHistory(history)
|
||||
case tcell.KeyLeft:
|
||||
if e.Modifiers() == tcell.ModCtrl {
|
||||
m.Start()
|
||||
} else if e.Modifiers() == tcell.ModAlt || e.Modifiers() == tcell.ModMeta {
|
||||
m.WordLeft()
|
||||
} else {
|
||||
m.CursorLeft()
|
||||
}
|
||||
case tcell.KeyRight:
|
||||
if e.Modifiers() == tcell.ModCtrl {
|
||||
m.End()
|
||||
} else if e.Modifiers() == tcell.ModAlt || e.Modifiers() == tcell.ModMeta {
|
||||
m.WordRight()
|
||||
} else {
|
||||
m.CursorRight()
|
||||
}
|
||||
case tcell.KeyBackspace2, tcell.KeyBackspace:
|
||||
if e.Modifiers() == tcell.ModCtrl || e.Modifiers() == tcell.ModAlt || e.Modifiers() == tcell.ModMeta {
|
||||
m.DeleteWordLeft()
|
||||
} else {
|
||||
m.Backspace()
|
||||
}
|
||||
case tcell.KeyCtrlW:
|
||||
m.DeleteWordLeft()
|
||||
case tcell.KeyCtrlV:
|
||||
m.Paste()
|
||||
case tcell.KeyCtrlF:
|
||||
m.WordRight()
|
||||
case tcell.KeyCtrlB:
|
||||
m.WordLeft()
|
||||
case tcell.KeyRune:
|
||||
m.response = Insert(m.response, m.cursorx, string(e.Rune()))
|
||||
m.cursorx++
|
||||
@@ -579,10 +463,8 @@ 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(posx, h-1, runes[x], nil, m.style)
|
||||
posx += runewidth.RuneWidth(runes[x])
|
||||
screen.SetContent(x, h-1, runes[x], nil, m.style)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -593,59 +475,6 @@ func (m *Messenger) Display() {
|
||||
}
|
||||
}
|
||||
|
||||
// LoadHistory attempts to load user history from configDir/buffers/history
|
||||
// into the history map
|
||||
// The savehistory option must be on
|
||||
func (m *Messenger) LoadHistory() {
|
||||
if GetGlobalOption("savehistory").(bool) {
|
||||
file, err := os.Open(configDir + "/buffers/history")
|
||||
var decodedMap map[string][]string
|
||||
if err == nil {
|
||||
decoder := gob.NewDecoder(file)
|
||||
err = decoder.Decode(&decodedMap)
|
||||
file.Close()
|
||||
|
||||
if err != nil {
|
||||
m.Error("Error loading history:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if decodedMap != nil {
|
||||
m.history = decodedMap
|
||||
} else {
|
||||
m.history = make(map[string][]string)
|
||||
}
|
||||
} else {
|
||||
m.history = make(map[string][]string)
|
||||
}
|
||||
}
|
||||
|
||||
// SaveHistory saves the user's command history to configDir/buffers/history
|
||||
// only if the savehistory option is on
|
||||
func (m *Messenger) SaveHistory() {
|
||||
if GetGlobalOption("savehistory").(bool) {
|
||||
// Don't save history past 100
|
||||
for k, v := range m.history {
|
||||
if len(v) > 100 {
|
||||
m.history[k] = v[len(m.history[k])-100:]
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Create(configDir + "/buffers/history")
|
||||
if err == nil {
|
||||
encoder := gob.NewEncoder(file)
|
||||
|
||||
err = encoder.Encode(m.history)
|
||||
if err != nil {
|
||||
m.Error("Error saving history:", err)
|
||||
return
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A GutterMessage is a message displayed on the side of the editor
|
||||
type GutterMessage struct {
|
||||
lineNum int
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
@@ -20,6 +21,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
synLinesUp = 75 // How many lines up to look to do syntax highlighting
|
||||
synLinesDown = 75 // How many lines down to look to do syntax highlighting
|
||||
doubleClickThreshold = 400 // How many milliseconds to wait before a second click is not a double click
|
||||
undoThreshold = 500 // If two events are less than n milliseconds apart, undo both of them
|
||||
autosaveTime = 8 // Number of seconds to wait before autosaving
|
||||
@@ -43,12 +46,14 @@ var (
|
||||
|
||||
// Version is the version number or commit hash
|
||||
// These variables should be set by the linker when compiling
|
||||
Version = "0.0.0-unknown"
|
||||
// CommitHash is the commit this version was built on
|
||||
CommitHash = "Unknown"
|
||||
// CompileDate is the date this binary was compiled on
|
||||
Version = "0.0.0-unknown"
|
||||
CommitHash = "Unknown"
|
||||
CompileDate = "Unknown"
|
||||
|
||||
// L is the lua state
|
||||
// This is the VM that runs the plugins
|
||||
L *lua.LState
|
||||
|
||||
// The list of views
|
||||
tabs []*Tab
|
||||
// This is the currently open tab
|
||||
@@ -58,10 +63,8 @@ var (
|
||||
// Channel of jobs running in the background
|
||||
jobs chan JobFunction
|
||||
// Event channel
|
||||
events chan tcell.Event
|
||||
autosave chan bool
|
||||
updateterm chan bool
|
||||
closeterm chan int
|
||||
events chan tcell.Event
|
||||
autosave chan bool
|
||||
)
|
||||
|
||||
// LoadInput determines which files should be loaded into buffers
|
||||
@@ -146,15 +149,6 @@ func InitConfigDir() {
|
||||
}
|
||||
configDir = xdgHome + "/micro"
|
||||
|
||||
if len(*flagConfigDir) > 0 {
|
||||
if _, err := os.Stat(*flagConfigDir); os.IsNotExist(err) {
|
||||
TermMessage("Error: " + *flagConfigDir + " does not exist. Defaulting to " + configDir + ".")
|
||||
} else {
|
||||
configDir = *flagConfigDir
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(xdgHome); os.IsNotExist(err) {
|
||||
// If the xdgHome doesn't exist we should create it
|
||||
err = os.Mkdir(xdgHome, os.ModePerm)
|
||||
@@ -205,11 +199,8 @@ func InitScreen() {
|
||||
os.Setenv("TERM", oldTerm)
|
||||
}
|
||||
|
||||
if GetGlobalOption("mouse").(bool) {
|
||||
screen.EnableMouse()
|
||||
}
|
||||
|
||||
// screen.SetStyle(defStyle)
|
||||
screen.SetStyle(defStyle)
|
||||
screen.EnableMouse()
|
||||
}
|
||||
|
||||
// RedrawAll redraws everything -- all the views and the messenger
|
||||
@@ -228,9 +219,6 @@ func RedrawAll() {
|
||||
}
|
||||
DisplayTabs()
|
||||
messenger.Display()
|
||||
if globalSettings["keymenu"].(bool) {
|
||||
DisplayKeyMenu()
|
||||
}
|
||||
screen.Show()
|
||||
}
|
||||
|
||||
@@ -256,29 +244,15 @@ func LoadAll() {
|
||||
}
|
||||
}
|
||||
|
||||
// Command line flags
|
||||
// Passing -version as a flag will have micro print out the version number
|
||||
var flagVersion = flag.Bool("version", false, "Show the version number and information")
|
||||
var flagStartPos = flag.String("startpos", "", "LINE,COL to start the cursor at when opening a buffer.")
|
||||
var flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
|
||||
var flagOptions = flag.Bool("options", false, "Show all option help")
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() {
|
||||
fmt.Println("Usage: micro [OPTIONS] [FILE]...")
|
||||
fmt.Println("-config-dir dir")
|
||||
fmt.Println(" \tSpecify a custom location for the configuration directory")
|
||||
fmt.Println("-startpos LINE,COL")
|
||||
fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
|
||||
fmt.Println("-options")
|
||||
fmt.Println(" \tShow all option help")
|
||||
fmt.Println("-version")
|
||||
fmt.Println(" \tShow the version number and information")
|
||||
|
||||
fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the settings.json\nfile (see 'help options').\n\n")
|
||||
fmt.Println("-option value")
|
||||
fmt.Println(" \tSet `option` to `value` for this session")
|
||||
fmt.Println(" \tFor example: `micro -syntax off file.c`")
|
||||
fmt.Println("\nUse `micro -options` to see the full list of configuration options")
|
||||
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.PrintDefaults()
|
||||
}
|
||||
|
||||
optionFlags := make(map[string]*string)
|
||||
@@ -297,15 +271,6 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if *flagOptions {
|
||||
// If -options was passed
|
||||
for k, v := range DefaultGlobalSettings() {
|
||||
fmt.Printf("-%s value\n", k)
|
||||
fmt.Printf(" \tThe %s option. Default value: '%v'\n", k, v)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Start the Lua VM for running plugins
|
||||
L = lua.NewState()
|
||||
defer L.Close()
|
||||
@@ -345,7 +310,7 @@ func main() {
|
||||
// Create a new messenger
|
||||
// This is used for sending the user messages in the bottom of the editor
|
||||
messenger = new(Messenger)
|
||||
messenger.LoadHistory()
|
||||
messenger.history = make(map[string][]string)
|
||||
|
||||
// Now we load the input
|
||||
buffers := LoadInput()
|
||||
@@ -390,12 +355,6 @@ func main() {
|
||||
L.SetGlobal("IsWordChar", luar.New(L, IsWordChar))
|
||||
L.SetGlobal("HandleCommand", luar.New(L, HandleCommand))
|
||||
L.SetGlobal("HandleShellCommand", luar.New(L, HandleShellCommand))
|
||||
L.SetGlobal("ExecCommand", luar.New(L, ExecCommand))
|
||||
L.SetGlobal("RunShellCommand", luar.New(L, RunShellCommand))
|
||||
L.SetGlobal("RunBackgroundShell", luar.New(L, RunBackgroundShell))
|
||||
L.SetGlobal("RunInteractiveShell", luar.New(L, RunInteractiveShell))
|
||||
L.SetGlobal("TermEmuSupported", luar.New(L, TermEmuSupported))
|
||||
L.SetGlobal("RunTermEmulator", luar.New(L, RunTermEmulator))
|
||||
L.SetGlobal("GetLeadingWhitespace", luar.New(L, GetLeadingWhitespace))
|
||||
L.SetGlobal("MakeCompletion", luar.New(L, MakeCompletion))
|
||||
L.SetGlobal("NewBuffer", luar.New(L, NewBufferFromString))
|
||||
@@ -405,7 +364,6 @@ func main() {
|
||||
L.SetGlobal("Loc", luar.New(L, func(x, y int) Loc {
|
||||
return Loc{x, y}
|
||||
}))
|
||||
L.SetGlobal("WorkingDirectory", luar.New(L, os.Getwd))
|
||||
L.SetGlobal("JoinPaths", luar.New(L, filepath.Join))
|
||||
L.SetGlobal("DirectoryName", luar.New(L, filepath.Dir))
|
||||
L.SetGlobal("configDir", luar.New(L, configDir))
|
||||
@@ -426,33 +384,30 @@ func main() {
|
||||
L.SetGlobal("AddRuntimeFilesFromDirectory", luar.New(L, PluginAddRuntimeFilesFromDirectory))
|
||||
L.SetGlobal("AddRuntimeFileFromMemory", luar.New(L, PluginAddRuntimeFileFromMemory))
|
||||
|
||||
// Access to Go stdlib
|
||||
L.SetGlobal("import", luar.New(L, Import))
|
||||
|
||||
jobs = make(chan JobFunction, 100)
|
||||
events = make(chan tcell.Event, 100)
|
||||
autosave = make(chan bool)
|
||||
updateterm = make(chan bool)
|
||||
closeterm = make(chan int)
|
||||
|
||||
LoadPlugins()
|
||||
|
||||
for _, t := range tabs {
|
||||
for _, v := range t.views {
|
||||
GlobalPluginCall("onViewOpen", v)
|
||||
GlobalPluginCall("onBufferOpen", v.Buf)
|
||||
for pl := range loadedPlugins {
|
||||
_, err := Call(pl+".onViewOpen", v)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
|
||||
TermMessage(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InitColorscheme()
|
||||
messenger.style = defStyle
|
||||
|
||||
// Here is the event loop which runs in a separate thread
|
||||
go func() {
|
||||
for {
|
||||
if screen != nil {
|
||||
events <- screen.PollEvent()
|
||||
}
|
||||
events <- screen.PollEvent()
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -478,19 +433,11 @@ func main() {
|
||||
f.function(f.output, f.args...)
|
||||
continue
|
||||
case <-autosave:
|
||||
if CurView().Buf.Path != "" {
|
||||
CurView().Save(true)
|
||||
}
|
||||
case <-updateterm:
|
||||
continue
|
||||
case vnum := <-closeterm:
|
||||
tabs[curTab].views[vnum].CloseTerminal()
|
||||
CurView().Save(true)
|
||||
case event = <-events:
|
||||
}
|
||||
|
||||
for event != nil {
|
||||
didAction := false
|
||||
|
||||
switch e := event.(type) {
|
||||
case *tcell.EventResize:
|
||||
for _, t := range tabs {
|
||||
@@ -520,38 +467,24 @@ func main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if e.Buttons() == tcell.WheelUp || e.Buttons() == tcell.WheelDown {
|
||||
var view *View
|
||||
x, y := e.Position()
|
||||
for _, v := range tabs[curTab].views {
|
||||
if x >= v.x && x < v.x+v.Width && y >= v.y && y < v.y+v.Height {
|
||||
view = tabs[curTab].views[v.Num]
|
||||
}
|
||||
}
|
||||
if view != nil {
|
||||
view.HandleEvent(e)
|
||||
didAction = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !didAction {
|
||||
// This function checks the mouse event for the possibility of changing the current tab
|
||||
// If the tab was changed it returns true
|
||||
if TabbarHandleMouseEvent(event) {
|
||||
break
|
||||
}
|
||||
// This function checks the mouse event for the possibility of changing the current tab
|
||||
// If the tab was changed it returns true
|
||||
if TabbarHandleMouseEvent(event) {
|
||||
break
|
||||
}
|
||||
|
||||
if searching {
|
||||
// Since searching is done in real time, we need to redraw every time
|
||||
// there is a new event in the search bar so we need a special function
|
||||
// to run instead of the standard HandleEvent.
|
||||
HandleSearchEvent(event, CurView())
|
||||
} else {
|
||||
// Send it to the view
|
||||
CurView().HandleEvent(event)
|
||||
}
|
||||
if searching {
|
||||
// Since searching is done in real time, we need to redraw every time
|
||||
// there is a new event in the search bar so we need a special function
|
||||
// to run instead of the standard HandleEvent.
|
||||
HandleSearchEvent(event, CurView())
|
||||
} else {
|
||||
// Send it to the view
|
||||
CurView().HandleEvent(event)
|
||||
}
|
||||
|
||||
select {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/yuin/gopher-lua"
|
||||
"github.com/zyedidia/tcell"
|
||||
"layeh.com/gopher-luar"
|
||||
)
|
||||
|
||||
@@ -61,20 +60,6 @@ func LuaFunctionBinding(function string) func(*View, bool) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// LuaFunctionMouseBinding is a function generator which takes the name of a lua function
|
||||
// and creates a function that will call that lua function
|
||||
// Specifically it creates a function that can be called as a mouse binding because this is used
|
||||
// to bind mouse actions to lua functions
|
||||
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 {
|
||||
@@ -118,13 +103,10 @@ func LuaFunctionComplete(function string) func(string) []string {
|
||||
}
|
||||
}
|
||||
|
||||
// LuaFunctionJob returns a function that will call the given lua function
|
||||
// structured as a job call i.e. the job output and arguments are provided
|
||||
// to the lua function
|
||||
func LuaFunctionJob(function string) func(string, ...string) {
|
||||
return func(output string, args ...string) {
|
||||
_, err := Call(function, unpack(append([]string{output}, args...))...)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
|
||||
if err != nil {
|
||||
TermMessage(err)
|
||||
}
|
||||
}
|
||||
@@ -137,9 +119,11 @@ func luaPluginName(name string) string {
|
||||
|
||||
// LoadPlugins loads the pre-installed plugins and the plugins located in ~/.config/micro/plugins
|
||||
func LoadPlugins() {
|
||||
|
||||
loadedPlugins = make(map[string]string)
|
||||
|
||||
for _, plugin := range ListRuntimeFiles(RTPlugin) {
|
||||
|
||||
pluginName := plugin.Name()
|
||||
if _, ok := loadedPlugins[pluginName]; ok {
|
||||
continue
|
||||
@@ -152,8 +136,9 @@ func LoadPlugins() {
|
||||
}
|
||||
|
||||
pluginLuaName := luaPluginName(pluginName)
|
||||
pluginDef := "\nlocal P = {}\n" + pluginLuaName + " = P\nsetmetatable(" + pluginLuaName + ", {__index = _G})\nsetfenv(1, P)\n"
|
||||
|
||||
if err := LoadFile(pluginLuaName, pluginLuaName, string(data)); err != nil {
|
||||
if err := L.DoString(pluginDef + string(data)); err != nil {
|
||||
TermMessage(err)
|
||||
continue
|
||||
}
|
||||
@@ -163,22 +148,11 @@ func LoadPlugins() {
|
||||
}
|
||||
|
||||
if _, err := os.Stat(configDir + "/init.lua"); err == nil {
|
||||
pluginDef := "\nlocal P = {}\n" + "init" + " = P\nsetmetatable(" + "init" + ", {__index = _G})\nsetfenv(1, P)\n"
|
||||
data, _ := ioutil.ReadFile(configDir + "/init.lua")
|
||||
if err := LoadFile("init", configDir+"init.lua", string(data)); err != nil {
|
||||
if err := L.DoString(pluginDef + string(data)); err != nil {
|
||||
TermMessage(err)
|
||||
}
|
||||
loadedPlugins["init"] = "init"
|
||||
}
|
||||
}
|
||||
|
||||
// GlobalCall makes a call to a function in every plugin that is currently
|
||||
// loaded
|
||||
func GlobalPluginCall(function string, args ...interface{}) {
|
||||
for pl := range loadedPlugins {
|
||||
_, err := Call(pl+"."+function, args...)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
|
||||
TermMessage(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/blang/semver"
|
||||
"github.com/flynn/json5"
|
||||
"github.com/yuin/gopher-lua"
|
||||
"github.com/zyedidia/json5/encoding/json5"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -422,7 +422,6 @@ func (pv *PluginVersion) DownloadAndInstall() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Install files and directory's
|
||||
for _, f := range z.File {
|
||||
parts := strings.Split(f.Name, "/")
|
||||
if allPrefixed {
|
||||
@@ -435,12 +434,6 @@ func (pv *PluginVersion) DownloadAndInstall() error {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
basepath := filepath.Dir(targetName)
|
||||
|
||||
if err := os.MkdirAll(basepath, dirPerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/blang/semver"
|
||||
"testing"
|
||||
|
||||
"github.com/blang/semver"
|
||||
|
||||
"github.com/flynn/json5"
|
||||
"github.com/zyedidia/json5/encoding/json5"
|
||||
)
|
||||
|
||||
func TestDependencyResolving(t *testing.T) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,20 +0,0 @@
|
||||
package main
|
||||
|
||||
// ScrollBar represents an optional scrollbar that can be used
|
||||
type ScrollBar struct {
|
||||
view *View
|
||||
}
|
||||
|
||||
// Display shows the scrollbar
|
||||
func (sb *ScrollBar) Display() {
|
||||
style := defStyle.Reverse(true)
|
||||
screen.SetContent(sb.view.x+sb.view.Width-1, sb.view.y+sb.pos(), ' ', nil, style)
|
||||
}
|
||||
|
||||
func (sb *ScrollBar) pos() int {
|
||||
numlines := sb.view.Buf.NumLines
|
||||
h := sb.view.Height
|
||||
filepercent := float32(sb.view.Topline) / float32(numlines)
|
||||
|
||||
return int(filepercent * float32(h))
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/zyedidia/tcell"
|
||||
)
|
||||
@@ -12,7 +11,7 @@ var (
|
||||
lastSearch string
|
||||
|
||||
// Where should we start the search down from (or up from)
|
||||
searchStart Loc
|
||||
searchStart int
|
||||
|
||||
// Is there currently a search in progress
|
||||
searching bool
|
||||
@@ -44,7 +43,7 @@ func EndSearch() {
|
||||
}
|
||||
}
|
||||
|
||||
// ExitSearch exits the search mode, reset active search phrase, and clear status bar
|
||||
// exit the search mode, reset active search phrase, and clear status bar
|
||||
func ExitSearch(v *View) {
|
||||
lastSearch = ""
|
||||
searching = false
|
||||
@@ -64,12 +63,7 @@ func HandleSearchEvent(event tcell.Event, v *View) {
|
||||
// Exit the search mode
|
||||
ExitSearch(v)
|
||||
return
|
||||
case tcell.KeyEnter:
|
||||
// If the user has pressed Enter, they want this to be the lastSearch
|
||||
lastSearch = messenger.response
|
||||
EndSearch()
|
||||
return
|
||||
case tcell.KeyCtrlQ, tcell.KeyCtrlC:
|
||||
case tcell.KeyCtrlQ, tcell.KeyCtrlC, tcell.KeyEnter:
|
||||
// Done
|
||||
EndSearch()
|
||||
return
|
||||
@@ -97,66 +91,6 @@ 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.OrigSelection[0] = v.Cursor.CurSelection[0]
|
||||
v.Cursor.OrigSelection[1] = v.Cursor.CurSelection[1]
|
||||
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.OrigSelection[0] = v.Cursor.CurSelection[0]
|
||||
v.Cursor.OrigSelection[1] = v.Cursor.CurSelection[1]
|
||||
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
|
||||
@@ -164,6 +98,15 @@ 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)
|
||||
@@ -171,20 +114,37 @@ 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
|
||||
}
|
||||
|
||||
var found bool
|
||||
if down {
|
||||
found = searchDown(r, v, searchStart, v.Buf.End())
|
||||
if !found {
|
||||
found = searchDown(r, v, v.Buf.Start(), searchStart)
|
||||
if !down {
|
||||
match = matches[len(matches)-1]
|
||||
} else {
|
||||
match = matches[0]
|
||||
}
|
||||
str = text
|
||||
}
|
||||
|
||||
if !down {
|
||||
match = matches[len(matches)-1]
|
||||
} else {
|
||||
found = searchUp(r, v, searchStart, v.Buf.Start())
|
||||
if !found {
|
||||
found = searchUp(r, v, v.Buf.End(), searchStart)
|
||||
}
|
||||
match = matches[0]
|
||||
}
|
||||
if !found {
|
||||
v.Cursor.ResetSelection()
|
||||
|
||||
if match[0] == match[1] {
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -10,8 +8,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/flynn/json5"
|
||||
"github.com/zyedidia/glob"
|
||||
"github.com/zyedidia/json5/encoding/json5"
|
||||
)
|
||||
|
||||
type optionValidator func(string, interface{}) error
|
||||
@@ -19,8 +17,6 @@ type optionValidator func(string, interface{}) error
|
||||
// The options that the user can set
|
||||
var globalSettings map[string]interface{}
|
||||
|
||||
var invalidSettings bool
|
||||
|
||||
// Options with validators
|
||||
var optionValidators = map[string]optionValidator{
|
||||
"tabsize": validatePositiveValue,
|
||||
@@ -28,12 +24,10 @@ var optionValidators = map[string]optionValidator{
|
||||
"scrollspeed": validateNonNegativeValue,
|
||||
"colorscheme": validateColorscheme,
|
||||
"colorcolumn": validateNonNegativeValue,
|
||||
"fileformat": validateLineEnding,
|
||||
}
|
||||
|
||||
// InitGlobalSettings initializes the options map and sets all options to their default values
|
||||
func InitGlobalSettings() {
|
||||
invalidSettings = false
|
||||
defaults := DefaultGlobalSettings()
|
||||
var parsed map[string]interface{}
|
||||
|
||||
@@ -44,14 +38,12 @@ func InitGlobalSettings() {
|
||||
if !strings.HasPrefix(string(input), "null") {
|
||||
if err != nil {
|
||||
TermMessage("Error reading settings.json file: " + err.Error())
|
||||
invalidSettings = true
|
||||
return
|
||||
}
|
||||
|
||||
err = json5.Unmarshal(input, &parsed)
|
||||
if err != nil {
|
||||
TermMessage("Error reading settings.json:", err.Error())
|
||||
invalidSettings = true
|
||||
}
|
||||
} else {
|
||||
writeSettings = true
|
||||
@@ -79,7 +71,6 @@ func InitGlobalSettings() {
|
||||
// InitLocalSettings scans the json in settings.json and sets the options locally based
|
||||
// on whether the buffer matches the glob
|
||||
func InitLocalSettings(buf *Buffer) {
|
||||
invalidSettings = false
|
||||
var parsed map[string]interface{}
|
||||
|
||||
filename := configDir + "/settings.json"
|
||||
@@ -87,36 +78,26 @@ func InitLocalSettings(buf *Buffer) {
|
||||
input, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
TermMessage("Error reading settings.json file: " + err.Error())
|
||||
invalidSettings = true
|
||||
return
|
||||
}
|
||||
|
||||
err = json5.Unmarshal(input, &parsed)
|
||||
if err != nil {
|
||||
TermMessage("Error reading settings.json:", err.Error())
|
||||
invalidSettings = true
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range parsed {
|
||||
if strings.HasPrefix(reflect.TypeOf(v).String(), "map") {
|
||||
if strings.HasPrefix(k, "ft:") {
|
||||
if buf.Settings["filetype"].(string) == k[3:] {
|
||||
for k1, v1 := range v.(map[string]interface{}) {
|
||||
buf.Settings[k1] = v1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
g, err := glob.Compile(k)
|
||||
if err != nil {
|
||||
TermMessage("Error with glob setting ", k, ": ", err)
|
||||
continue
|
||||
}
|
||||
g, err := glob.Compile(k)
|
||||
if err != nil {
|
||||
TermMessage("Error with glob setting ", k, ": ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if g.MatchString(buf.Path) {
|
||||
for k1, v1 := range v.(map[string]interface{}) {
|
||||
buf.Settings[k1] = v1
|
||||
}
|
||||
if g.MatchString(buf.Path) {
|
||||
for k1, v1 := range v.(map[string]interface{}) {
|
||||
buf.Settings[k1] = v1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,11 +106,6 @@ func InitLocalSettings(buf *Buffer) {
|
||||
|
||||
// WriteSettings writes the settings to the specified filename as JSON
|
||||
func WriteSettings(filename string) error {
|
||||
if invalidSettings {
|
||||
// Do not write the settings if there was an error when reading them
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if _, e := os.Stat(configDir); e == nil {
|
||||
parsed := make(map[string]interface{})
|
||||
@@ -148,7 +124,6 @@ func WriteSettings(filename string) error {
|
||||
err = json5.Unmarshal(input, &parsed)
|
||||
if err != nil {
|
||||
TermMessage("Error reading settings.json:", err.Error())
|
||||
invalidSettings = true
|
||||
}
|
||||
|
||||
for k, v := range parsed {
|
||||
@@ -161,7 +136,7 @@ func WriteSettings(filename string) error {
|
||||
}
|
||||
}
|
||||
|
||||
txt, _ := json.MarshalIndent(parsed, "", " ")
|
||||
txt, _ := json5.MarshalIndent(parsed, "", " ")
|
||||
err = ioutil.WriteFile(filename, append(txt, '\n'), 0644)
|
||||
}
|
||||
return err
|
||||
@@ -201,42 +176,35 @@ func GetOption(name string) interface{} {
|
||||
func DefaultGlobalSettings() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"autoindent": true,
|
||||
"keepautoindent": false,
|
||||
"autosave": false,
|
||||
"basename": false,
|
||||
"colorcolumn": float64(0),
|
||||
"colorscheme": "default",
|
||||
"cursorline": true,
|
||||
"eofnewline": false,
|
||||
"fastdirty": true,
|
||||
"fileformat": "unix",
|
||||
"rmtrailingws": false,
|
||||
"ignorecase": false,
|
||||
"indentchar": " ",
|
||||
"infobar": true,
|
||||
"keepautoindent": false,
|
||||
"keymenu": false,
|
||||
"matchbrace": false,
|
||||
"mouse": true,
|
||||
"pluginchannels": []string{"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"},
|
||||
"pluginrepos": []string{},
|
||||
"rmtrailingws": false,
|
||||
"ruler": true,
|
||||
"savecursor": false,
|
||||
"savehistory": true,
|
||||
"saveundo": false,
|
||||
"scrollbar": false,
|
||||
"scrollmargin": float64(3),
|
||||
"scrollspeed": float64(2),
|
||||
"scrollmargin": float64(3),
|
||||
"softwrap": false,
|
||||
"splitbottom": true,
|
||||
"splitright": true,
|
||||
"splitRight": true,
|
||||
"splitBottom": true,
|
||||
"statusline": true,
|
||||
"sucmd": "sudo",
|
||||
"syntax": true,
|
||||
"tabmovement": false,
|
||||
"tabsize": float64(4),
|
||||
"tabstospaces": false,
|
||||
"termtitle": false,
|
||||
"useprimary": true,
|
||||
"pluginchannels": []string{
|
||||
"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json",
|
||||
},
|
||||
"pluginrepos": []string{},
|
||||
"useprimary": true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,28 +213,23 @@ func DefaultGlobalSettings() map[string]interface{} {
|
||||
func DefaultLocalSettings() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"autoindent": true,
|
||||
"keepautoindent": false,
|
||||
"autosave": false,
|
||||
"basename": false,
|
||||
"colorcolumn": float64(0),
|
||||
"cursorline": true,
|
||||
"eofnewline": false,
|
||||
"fastdirty": true,
|
||||
"fileformat": "unix",
|
||||
"rmtrailingws": false,
|
||||
"filetype": "Unknown",
|
||||
"ignorecase": false,
|
||||
"indentchar": " ",
|
||||
"keepautoindent": false,
|
||||
"matchbrace": false,
|
||||
"rmtrailingws": false,
|
||||
"ruler": true,
|
||||
"savecursor": false,
|
||||
"saveundo": false,
|
||||
"scrollbar": false,
|
||||
"scrollmargin": float64(3),
|
||||
"scrollspeed": float64(2),
|
||||
"scrollmargin": float64(3),
|
||||
"softwrap": false,
|
||||
"splitbottom": true,
|
||||
"splitright": true,
|
||||
"splitRight": true,
|
||||
"splitBottom": true,
|
||||
"statusline": true,
|
||||
"syntax": true,
|
||||
"tabmovement": false,
|
||||
@@ -326,26 +289,16 @@ func SetOption(option, value string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if option == "infobar" || option == "keymenu" {
|
||||
if option == "infobar" {
|
||||
for _, tab := range tabs {
|
||||
tab.Resize()
|
||||
}
|
||||
}
|
||||
|
||||
if option == "mouse" {
|
||||
if !nativeValue.(bool) {
|
||||
screen.DisableMouse()
|
||||
} else {
|
||||
screen.EnableMouse()
|
||||
}
|
||||
}
|
||||
|
||||
if len(tabs) != 0 {
|
||||
if _, ok := CurView().Buf.Settings[option]; ok {
|
||||
for _, tab := range tabs {
|
||||
for _, view := range tab.views {
|
||||
SetLocalOption(option, value, view)
|
||||
}
|
||||
if _, ok := CurView().Buf.Settings[option]; ok {
|
||||
for _, tab := range tabs {
|
||||
for _, view := range tab.views {
|
||||
SetLocalOption(option, value, view)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -385,26 +338,6 @@ func SetLocalOption(option, value string, view *View) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if option == "fastdirty" {
|
||||
// If it is being turned off, we have to hash every open buffer
|
||||
var empty [16]byte
|
||||
for _, tab := range tabs {
|
||||
for _, v := range tab.views {
|
||||
if !nativeValue.(bool) {
|
||||
if v.Buf.origHash == empty {
|
||||
data, err := ioutil.ReadFile(v.Buf.AbsPath)
|
||||
if err != nil {
|
||||
data = []byte{}
|
||||
}
|
||||
v.Buf.origHash = md5.Sum(data)
|
||||
}
|
||||
} else {
|
||||
v.Buf.IsModified = v.Buf.Modified()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.Settings[option] = nativeValue
|
||||
|
||||
if option == "statusline" {
|
||||
@@ -417,10 +350,6 @@ func SetLocalOption(option, value string, view *View) error {
|
||||
buf.UpdateRules()
|
||||
}
|
||||
|
||||
if option == "fileformat" {
|
||||
buf.IsModified = true
|
||||
}
|
||||
|
||||
if option == "syntax" {
|
||||
if !nativeValue.(bool) {
|
||||
buf.ClearMatches()
|
||||
@@ -501,17 +430,3 @@ func validateColorscheme(option string, value interface{}) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateLineEnding(option string, value interface{}) error {
|
||||
endingType, ok := value.(string)
|
||||
|
||||
if !ok {
|
||||
return errors.New("Expected string type for file format")
|
||||
}
|
||||
|
||||
if endingType != "unix" && endingType != "dos" {
|
||||
return errors.New("File format must be either 'unix' or 'dos'")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"strings"
|
||||
|
||||
"github.com/zyedidia/micro/cmd/micro/shellwords"
|
||||
)
|
||||
|
||||
// ExecCommand executes a command using exec
|
||||
// It returns any output/errors
|
||||
func ExecCommand(name string, arg ...string) (string, error) {
|
||||
var err error
|
||||
cmd := exec.Command(name, arg...)
|
||||
outputBytes := &bytes.Buffer{}
|
||||
cmd.Stdout = outputBytes
|
||||
cmd.Stderr = outputBytes
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = cmd.Wait() // wait for command to finish
|
||||
outstring := outputBytes.String()
|
||||
return outstring, err
|
||||
}
|
||||
|
||||
// RunShellCommand executes a shell command and returns the output/error
|
||||
func RunShellCommand(input string) (string, error) {
|
||||
args, err := shellwords.Split(input)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
inputCmd := args[0]
|
||||
|
||||
return ExecCommand(inputCmd, args[1:]...)
|
||||
}
|
||||
|
||||
func RunBackgroundShell(input string) {
|
||||
args, err := shellwords.Split(input)
|
||||
if err != nil {
|
||||
messenger.Error(err)
|
||||
return
|
||||
}
|
||||
inputCmd := args[0]
|
||||
messenger.Message("Running...")
|
||||
go func() {
|
||||
output, err := RunShellCommand(input)
|
||||
totalLines := strings.Split(output, "\n")
|
||||
|
||||
if len(totalLines) < 3 {
|
||||
if err == nil {
|
||||
messenger.Message(inputCmd, " exited without error")
|
||||
} else {
|
||||
messenger.Message(inputCmd, " exited with error: ", err, ": ", output)
|
||||
}
|
||||
} else {
|
||||
messenger.Message(output)
|
||||
}
|
||||
// We have to make sure to redraw
|
||||
RedrawAll()
|
||||
}()
|
||||
}
|
||||
|
||||
func RunInteractiveShell(input string, wait bool, getOutput bool) (string, error) {
|
||||
args, err := shellwords.Split(input)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
inputCmd := args[0]
|
||||
|
||||
// Shut down the screen because we're going to interact directly with the shell
|
||||
screen.Fini()
|
||||
screen = nil
|
||||
|
||||
args = args[1:]
|
||||
|
||||
// Set up everything for the command
|
||||
outputBytes := &bytes.Buffer{}
|
||||
cmd := exec.Command(inputCmd, args...)
|
||||
cmd.Stdin = os.Stdin
|
||||
if getOutput {
|
||||
cmd.Stdout = io.MultiWriter(os.Stdout, outputBytes)
|
||||
} else {
|
||||
cmd.Stdout = os.Stdout
|
||||
}
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
// This is a trap for Ctrl-C so that it doesn't kill micro
|
||||
// Instead we trap Ctrl-C to kill the program we're running
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
for range c {
|
||||
cmd.Process.Kill()
|
||||
}
|
||||
}()
|
||||
|
||||
cmd.Start()
|
||||
err = cmd.Wait()
|
||||
|
||||
output := outputBytes.String()
|
||||
|
||||
if wait {
|
||||
// This is just so we don't return right away and let the user press enter to return
|
||||
TermMessage("")
|
||||
}
|
||||
|
||||
// Start the screen back up
|
||||
InitScreen()
|
||||
|
||||
return output, err
|
||||
}
|
||||
|
||||
// HandleShellCommand runs the shell command
|
||||
// The openTerm argument specifies whether a terminal should be opened (for viewing output
|
||||
// or interacting with stdin)
|
||||
func HandleShellCommand(input string, openTerm bool, waitToFinish bool) string {
|
||||
if !openTerm {
|
||||
RunBackgroundShell(input)
|
||||
return ""
|
||||
} else {
|
||||
output, _ := RunInteractiveShell(input, waitToFinish, false)
|
||||
return output
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// +build linux darwin dragonfly openbsd_amd64 freebsd
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/zyedidia/micro/cmd/micro/shellwords"
|
||||
)
|
||||
|
||||
const TermEmuSupported = true
|
||||
|
||||
func RunTermEmulator(input string, wait bool, getOutput bool, callback string) error {
|
||||
args, err := shellwords.Split(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = CurView().StartTerminal(args, wait, getOutput, callback)
|
||||
return err
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd_amd64
|
||||
|
||||
package main
|
||||
|
||||
import "errors"
|
||||
|
||||
const TermEmuSupported = false
|
||||
|
||||
func RunTermEmulator(input string, wait bool, getOutput bool) error {
|
||||
return errors.New("Unsupported operating system")
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
This is a modified version of `go-shellwords` for the micro editor.
|
||||
|
||||
# go-shellwords
|
||||
|
||||
[](https://coveralls.io/r/mattn/go-shellwords?branch=master)
|
||||
[](https://travis-ci.org/mattn/go-shellwords)
|
||||
|
||||
Parse line as shell words.
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
args, err := shellwords.Parse("./foo --bar=baz")
|
||||
// args should be ["./foo", "--bar=baz"]
|
||||
```
|
||||
|
||||
```go
|
||||
os.Setenv("FOO", "bar")
|
||||
p := shellwords.NewParser()
|
||||
p.ParseEnv = true
|
||||
args, err := p.Parse("./foo $FOO")
|
||||
// args should be ["./foo", "bar"]
|
||||
```
|
||||
|
||||
```go
|
||||
p := shellwords.NewParser()
|
||||
p.ParseBacktick = true
|
||||
args, err := p.Parse("./foo `echo $SHELL`")
|
||||
// args should be ["./foo", "/bin/bash"]
|
||||
```
|
||||
|
||||
```go
|
||||
shellwords.ParseBacktick = true
|
||||
p := shellwords.NewParser()
|
||||
args, err := p.Parse("./foo `echo $SHELL`")
|
||||
// args should be ["./foo", "/bin/bash"]
|
||||
```
|
||||
|
||||
# Thanks
|
||||
|
||||
This is based on cpan module [Parse::CommandLine](https://metacpan.org/pod/Parse::CommandLine).
|
||||
|
||||
# License
|
||||
|
||||
under the MIT License: http://mattn.mit-license.org/2017
|
||||
|
||||
# Author
|
||||
|
||||
Yasuhiro Matsumoto (a.k.a mattn)
|
||||
@@ -1,180 +0,0 @@
|
||||
package shellwords
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var envRe = regexp.MustCompile(`\$({[a-zA-Z0-9_]+}|[a-zA-Z0-9_]+)`)
|
||||
|
||||
func isSpace(r rune) bool {
|
||||
switch r {
|
||||
case ' ', '\t', '\r', '\n':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func replaceEnv(s string) string {
|
||||
return envRe.ReplaceAllStringFunc(s, func(s string) string {
|
||||
s = s[1:]
|
||||
if s[0] == '{' {
|
||||
s = s[1 : len(s)-1]
|
||||
}
|
||||
return os.Getenv(s)
|
||||
})
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
Position int
|
||||
}
|
||||
|
||||
func NewParser() *Parser {
|
||||
return &Parser{0}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(line string) ([]string, error) {
|
||||
args := []string{}
|
||||
buf := ""
|
||||
var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool
|
||||
backtick := ""
|
||||
|
||||
pos := -1
|
||||
got := false
|
||||
|
||||
loop:
|
||||
for i, r := range line {
|
||||
if escaped {
|
||||
buf += string(r)
|
||||
escaped = false
|
||||
continue
|
||||
}
|
||||
|
||||
if r == '\\' {
|
||||
if singleQuoted {
|
||||
buf += string(r)
|
||||
} else {
|
||||
escaped = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if isSpace(r) {
|
||||
if singleQuoted || doubleQuoted || backQuote || dollarQuote {
|
||||
buf += string(r)
|
||||
backtick += string(r)
|
||||
} else if got {
|
||||
buf = replaceEnv(buf)
|
||||
args = append(args, buf)
|
||||
buf = ""
|
||||
got = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '`':
|
||||
if !singleQuoted && !doubleQuoted && !dollarQuote {
|
||||
if backQuote {
|
||||
out, err := shellRun(backtick)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf = out
|
||||
}
|
||||
backtick = ""
|
||||
backQuote = !backQuote
|
||||
continue
|
||||
backtick = ""
|
||||
backQuote = !backQuote
|
||||
}
|
||||
case ')':
|
||||
if !singleQuoted && !doubleQuoted && !backQuote {
|
||||
if dollarQuote {
|
||||
out, err := shellRun(backtick)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf = out
|
||||
}
|
||||
backtick = ""
|
||||
dollarQuote = !dollarQuote
|
||||
continue
|
||||
backtick = ""
|
||||
dollarQuote = !dollarQuote
|
||||
}
|
||||
case '(':
|
||||
if !singleQuoted && !doubleQuoted && !backQuote {
|
||||
if !dollarQuote && len(buf) > 0 && buf == "$" {
|
||||
dollarQuote = true
|
||||
buf += "("
|
||||
continue
|
||||
} else {
|
||||
return nil, errors.New("invalid command line string")
|
||||
}
|
||||
}
|
||||
case '"':
|
||||
if !singleQuoted && !dollarQuote {
|
||||
doubleQuoted = !doubleQuoted
|
||||
continue
|
||||
}
|
||||
case '\'':
|
||||
if !doubleQuoted && !dollarQuote {
|
||||
singleQuoted = !singleQuoted
|
||||
continue
|
||||
}
|
||||
case ';', '&', '|', '<', '>':
|
||||
if !(escaped || singleQuoted || doubleQuoted || backQuote) {
|
||||
pos = i
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
got = true
|
||||
buf += string(r)
|
||||
if backQuote || dollarQuote {
|
||||
backtick += string(r)
|
||||
}
|
||||
}
|
||||
|
||||
buf = replaceEnv(buf)
|
||||
args = append(args, buf)
|
||||
|
||||
if escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote {
|
||||
return nil, errors.New("invalid command line string")
|
||||
}
|
||||
|
||||
p.Position = pos
|
||||
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func Split(line string) ([]string, error) {
|
||||
return NewParser().Parse(line)
|
||||
}
|
||||
|
||||
func Join(args ...string) string {
|
||||
var buf bytes.Buffer
|
||||
for i, w := range args {
|
||||
if i != 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
if w == "" {
|
||||
buf.WriteString("''")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, b := range w {
|
||||
switch b {
|
||||
case ' ', '\t', '\r', '\n':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteString(string(b))
|
||||
default:
|
||||
buf.WriteString(string(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
package shellwords
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testcases = []struct {
|
||||
line string
|
||||
expected []string
|
||||
}{
|
||||
{`var --bar=baz`, []string{`var`, `--bar=baz`}},
|
||||
{`var --bar="baz"`, []string{`var`, `--bar=baz`}},
|
||||
{`var "--bar=baz"`, []string{`var`, `--bar=baz`}},
|
||||
{`var "--bar='baz'"`, []string{`var`, `--bar='baz'`}},
|
||||
{"var --bar=`baz`", []string{`var`, "--bar=`baz`"}},
|
||||
{`var "--bar=\"baz'"`, []string{`var`, `--bar="baz'`}},
|
||||
{`var "--bar=\'baz\'"`, []string{`var`, `--bar='baz'`}},
|
||||
{`var --bar='\'`, []string{`var`, `--bar=\`}},
|
||||
{`var "--bar baz"`, []string{`var`, `--bar baz`}},
|
||||
{`var --"bar baz"`, []string{`var`, `--bar baz`}},
|
||||
{`var --"bar baz"`, []string{`var`, `--bar baz`}},
|
||||
}
|
||||
|
||||
func TestSimple(t *testing.T) {
|
||||
for _, testcase := range testcases {
|
||||
args, err := Parse(testcase.line)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(args, testcase.expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", testcase.expected, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
_, err := Parse("foo '")
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
_, err = Parse(`foo "`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
|
||||
_, err = Parse("foo `")
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLastSpace(t *testing.T) {
|
||||
args, err := Parse("foo bar\\ ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(args) != 2 {
|
||||
t.Fatal("Should have two elements")
|
||||
}
|
||||
if args[0] != "foo" {
|
||||
t.Fatal("1st element should be `foo`")
|
||||
}
|
||||
if args[1] != "bar " {
|
||||
t.Fatal("1st element should be `bar `")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBacktick(t *testing.T) {
|
||||
goversion, err := shellRun("go version")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parser := NewParser()
|
||||
parser.ParseBacktick = true
|
||||
args, err := parser.Parse("echo `go version`")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []string{"echo", goversion}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
|
||||
args, err = parser.Parse(`echo $(echo foo)`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected = []string{"echo", "foo"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
|
||||
parser.ParseBacktick = false
|
||||
args, err = parser.Parse(`echo $(echo "foo")`)
|
||||
expected = []string{"echo", `$(echo "foo")`}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
args, err = parser.Parse("echo $(`echo1)")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected = []string{"echo", "$(`echo1)"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBacktickError(t *testing.T) {
|
||||
parser := NewParser()
|
||||
parser.ParseBacktick = true
|
||||
_, err := parser.Parse("echo `go Version`")
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
expected := "exit status 2:go: unknown subcommand \"Version\"\nRun 'go help' for usage.\n"
|
||||
if expected != err.Error() {
|
||||
t.Fatalf("Expected %q, but %q", expected, err.Error())
|
||||
}
|
||||
_, err = parser.Parse(`echo $(echo1)`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
_, err = parser.Parse(`echo $(echo1`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
_, err = parser.Parse(`echo $ (echo1`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
_, err = parser.Parse(`echo (echo1`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
_, err = parser.Parse(`echo )echo1`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnv(t *testing.T) {
|
||||
os.Setenv("FOO", "bar")
|
||||
|
||||
parser := NewParser()
|
||||
parser.ParseEnv = true
|
||||
args, err := parser.Parse("echo $FOO")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []string{"echo", "bar"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoEnv(t *testing.T) {
|
||||
parser := NewParser()
|
||||
parser.ParseEnv = true
|
||||
args, err := parser.Parse("echo $BAR")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []string{"echo", ""}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDupEnv(t *testing.T) {
|
||||
os.Setenv("FOO", "bar")
|
||||
os.Setenv("FOO_BAR", "baz")
|
||||
|
||||
parser := NewParser()
|
||||
parser.ParseEnv = true
|
||||
args, err := parser.Parse("echo $$FOO$")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []string{"echo", "$bar$"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
|
||||
args, err = parser.Parse("echo $${FOO_BAR}$")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected = []string{"echo", "$baz$"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHaveMore(t *testing.T) {
|
||||
parser := NewParser()
|
||||
parser.ParseEnv = true
|
||||
|
||||
line := "echo foo; seq 1 10"
|
||||
args, err := parser.Parse(line)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
expected := []string{"echo", "foo"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
|
||||
if parser.Position == 0 {
|
||||
t.Fatalf("Commands should be remaining")
|
||||
}
|
||||
|
||||
line = string([]rune(line)[parser.Position+1:])
|
||||
args, err = parser.Parse(line)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
expected = []string{"seq", "1", "10"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
|
||||
if parser.Position > 0 {
|
||||
t.Fatalf("Commands should not be remaining")
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package shellwords
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func shellRun(line string) (string, error) {
|
||||
shell := os.Getenv("SHELL")
|
||||
b, err := exec.Command(shell, "-c", line).Output()
|
||||
if err != nil {
|
||||
if eerr, ok := err.(*exec.ExitError); ok {
|
||||
b = eerr.Stderr
|
||||
}
|
||||
return "", errors.New(err.Error() + ":" + string(b))
|
||||
}
|
||||
return strings.TrimSpace(string(b)), nil
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package shellwords
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func shellRun(line string) (string, error) {
|
||||
shell := os.Getenv("COMSPEC")
|
||||
b, err := exec.Command(shell, "/c", line).Output()
|
||||
if err != nil {
|
||||
if eerr, ok := err.(*exec.ExitError); ok {
|
||||
b = eerr.Stderr
|
||||
}
|
||||
return "", errors.New(err.Error() + ":" + string(b))
|
||||
}
|
||||
return strings.TrimSpace(string(b)), nil
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -15,20 +14,13 @@ type Statusline struct {
|
||||
|
||||
// Display draws the statusline to the screen
|
||||
func (sline *Statusline) Display() {
|
||||
if messenger.hasPrompt && !GetGlobalOption("infobar").(bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// We'll draw the line at the lowest line in the view
|
||||
y := sline.view.Height + sline.view.y
|
||||
|
||||
file := sline.view.Buf.GetName()
|
||||
if sline.view.Buf.Settings["basename"].(bool) {
|
||||
file = path.Base(file)
|
||||
}
|
||||
|
||||
// If the buffer is dirty (has been modified) write a little '+'
|
||||
if sline.view.Buf.Modified() {
|
||||
if sline.view.Buf.IsModified {
|
||||
file += " +"
|
||||
}
|
||||
|
||||
@@ -44,27 +36,13 @@ func (sline *Statusline) Display() {
|
||||
// Add the filetype
|
||||
file += " " + sline.view.Buf.FileType()
|
||||
|
||||
file += " " + sline.view.Buf.Settings["fileformat"].(string)
|
||||
|
||||
rightText := ""
|
||||
if len(kmenuBinding) > 0 {
|
||||
if globalSettings["keymenu"].(bool) {
|
||||
rightText += kmenuBinding + ": hide bindings"
|
||||
} else {
|
||||
rightText += kmenuBinding + ": show bindings"
|
||||
}
|
||||
}
|
||||
if len(helpBinding) > 0 {
|
||||
if len(kmenuBinding) > 0 {
|
||||
rightText += ", "
|
||||
}
|
||||
rightText = helpBinding + " for help "
|
||||
if sline.view.Type == vtHelp {
|
||||
rightText += helpBinding + ": close help"
|
||||
} else {
|
||||
rightText += helpBinding + ": open help"
|
||||
rightText = helpBinding + " to close help "
|
||||
}
|
||||
}
|
||||
rightText += " "
|
||||
|
||||
statusLineStyle := defStyle.Reverse(true)
|
||||
if style, ok := colorscheme["statusline"]; ok {
|
||||
@@ -73,12 +51,6 @@ func (sline *Statusline) Display() {
|
||||
|
||||
// Maybe there is a unicode filename?
|
||||
fileRunes := []rune(file)
|
||||
|
||||
if sline.view.Type == vtTerm {
|
||||
fileRunes = []rune(sline.view.term.title)
|
||||
rightText = ""
|
||||
}
|
||||
|
||||
viewX := sline.view.x
|
||||
if viewX != 0 {
|
||||
screen.SetContent(viewX, y, ' ', nil, statusLineStyle)
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
|
||||
var tabBarOffset int
|
||||
|
||||
// A Tab holds an array of views and a splitTree to determine how the
|
||||
// views should be arranged
|
||||
type Tab struct {
|
||||
// This contains all the views in this tab
|
||||
// There is generally only one view per tab, but you can have
|
||||
@@ -38,9 +36,6 @@ func NewTabFromView(v *View) *Tab {
|
||||
if globalSettings["infobar"].(bool) {
|
||||
t.tree.height--
|
||||
}
|
||||
if globalSettings["keymenu"].(bool) {
|
||||
t.tree.height -= 2
|
||||
}
|
||||
|
||||
t.Resize()
|
||||
|
||||
@@ -55,13 +50,10 @@ func (t *Tab) SetNum(num int) {
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup cleans up the tree (for example if views have closed)
|
||||
func (t *Tab) Cleanup() {
|
||||
t.tree.Cleanup()
|
||||
}
|
||||
|
||||
// Resize handles a resize event from the terminal and resizes
|
||||
// all child views correctly
|
||||
func (t *Tab) Resize() {
|
||||
w, h := screen.Size()
|
||||
t.tree.width = w
|
||||
@@ -70,17 +62,11 @@ func (t *Tab) Resize() {
|
||||
if globalSettings["infobar"].(bool) {
|
||||
t.tree.height--
|
||||
}
|
||||
if globalSettings["keymenu"].(bool) {
|
||||
t.tree.height -= 2
|
||||
}
|
||||
|
||||
t.tree.ResizeSplits()
|
||||
|
||||
for i, v := range t.views {
|
||||
v.Num = i
|
||||
if v.Type == vtTerm {
|
||||
v.term.Resize(v.Width, v.Height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +91,7 @@ func TabbarString() (string, map[int]int) {
|
||||
}
|
||||
buf := t.views[t.CurView].Buf
|
||||
str += buf.GetName()
|
||||
if buf.Modified() {
|
||||
if buf.IsModified {
|
||||
str += " +"
|
||||
}
|
||||
if i == curTab {
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/zyedidia/clipboard"
|
||||
"github.com/zyedidia/tcell"
|
||||
"github.com/zyedidia/terminal"
|
||||
)
|
||||
|
||||
const (
|
||||
VTIdle = iota // Waiting for a new command
|
||||
VTRunning // Currently running a command
|
||||
VTDone // Finished running a command
|
||||
)
|
||||
|
||||
// A Terminal holds information for the terminal emulator
|
||||
type Terminal struct {
|
||||
state terminal.State
|
||||
view *View
|
||||
vtOld ViewType
|
||||
term *terminal.VT
|
||||
title string
|
||||
status int
|
||||
selection [2]Loc
|
||||
wait bool
|
||||
getOutput bool
|
||||
output *bytes.Buffer
|
||||
callback string
|
||||
}
|
||||
|
||||
// HasSelection returns whether this terminal has a valid selection
|
||||
func (t *Terminal) HasSelection() bool {
|
||||
return t.selection[0] != t.selection[1]
|
||||
}
|
||||
|
||||
// GetSelection returns the selected text
|
||||
func (t *Terminal) GetSelection(width int) string {
|
||||
start := t.selection[0]
|
||||
end := t.selection[1]
|
||||
if start.GreaterThan(end) {
|
||||
start, end = end, start
|
||||
}
|
||||
var ret string
|
||||
var l Loc
|
||||
for y := start.Y; y <= end.Y; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
l.X, l.Y = x, y
|
||||
if l.GreaterEqual(start) && l.LessThan(end) {
|
||||
c, _, _ := t.state.Cell(x, y)
|
||||
ret += string(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Start begins a new command in this terminal with a given view
|
||||
func (t *Terminal) Start(execCmd []string, view *View, getOutput bool) error {
|
||||
if len(execCmd) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := exec.Command(execCmd[0], execCmd[1:]...)
|
||||
t.output = nil
|
||||
if getOutput {
|
||||
t.output = bytes.NewBuffer([]byte{})
|
||||
}
|
||||
term, _, err := terminal.Start(&t.state, cmd, t.output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.term = term
|
||||
t.view = view
|
||||
t.getOutput = getOutput
|
||||
t.vtOld = view.Type
|
||||
t.status = VTRunning
|
||||
t.title = execCmd[0] + ":" + strconv.Itoa(cmd.Process.Pid)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
err := term.Parse()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "[Press enter to close]")
|
||||
break
|
||||
}
|
||||
updateterm <- true
|
||||
}
|
||||
closeterm <- view.Num
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Resize informs the terminal of a resize event
|
||||
func (t *Terminal) Resize(width, height int) {
|
||||
t.term.Resize(width, height)
|
||||
}
|
||||
|
||||
// HandleEvent handles a tcell event by forwarding it to the terminal emulator
|
||||
// If the event is a mouse event and the program running in the emulator
|
||||
// does not have mouse support, the emulator will support selections and
|
||||
// copy-paste
|
||||
func (t *Terminal) HandleEvent(event tcell.Event) {
|
||||
if e, ok := event.(*tcell.EventKey); ok {
|
||||
if t.status == VTDone {
|
||||
switch e.Key() {
|
||||
case tcell.KeyEscape, tcell.KeyCtrlQ, tcell.KeyEnter:
|
||||
t.Close()
|
||||
t.view.Type = vtDefault
|
||||
default:
|
||||
}
|
||||
}
|
||||
if e.Key() == tcell.KeyCtrlC && t.HasSelection() {
|
||||
clipboard.WriteAll(t.GetSelection(t.view.Width), "clipboard")
|
||||
messenger.Message("Copied selection to clipboard")
|
||||
} else if t.status != VTDone {
|
||||
t.WriteString(event.EscSeq())
|
||||
}
|
||||
} else if e, ok := event.(*tcell.EventMouse); !ok || t.state.Mode(terminal.ModeMouseMask) {
|
||||
t.WriteString(event.EscSeq())
|
||||
} else {
|
||||
x, y := e.Position()
|
||||
x -= t.view.x
|
||||
y += t.view.y
|
||||
|
||||
if e.Buttons() == tcell.Button1 {
|
||||
if !t.view.mouseReleased {
|
||||
// drag
|
||||
t.selection[1].X = x
|
||||
t.selection[1].Y = y
|
||||
} else {
|
||||
t.selection[0].X = x
|
||||
t.selection[0].Y = y
|
||||
t.selection[1].X = x
|
||||
t.selection[1].Y = y
|
||||
}
|
||||
|
||||
t.view.mouseReleased = false
|
||||
} else if e.Buttons() == tcell.ButtonNone {
|
||||
if !t.view.mouseReleased {
|
||||
t.selection[1].X = x
|
||||
t.selection[1].Y = y
|
||||
}
|
||||
t.view.mouseReleased = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops execution of the terminal and sets the status
|
||||
// to VTDone
|
||||
func (t *Terminal) Stop() {
|
||||
t.term.File().Close()
|
||||
t.term.Close()
|
||||
if t.wait {
|
||||
t.status = VTDone
|
||||
} else {
|
||||
t.Close()
|
||||
t.view.Type = t.vtOld
|
||||
}
|
||||
}
|
||||
|
||||
// Close sets the status to VTIdle indicating that the terminal
|
||||
// is ready for a new command to execute
|
||||
func (t *Terminal) Close() {
|
||||
t.status = VTIdle
|
||||
// call the lua function that the user has given as a callback
|
||||
if t.getOutput {
|
||||
_, err := Call(t.callback, t.output.String())
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
|
||||
TermMessage(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WriteString writes a given string to this terminal's pty
|
||||
func (t *Terminal) WriteString(str string) {
|
||||
t.term.File().WriteString(str)
|
||||
}
|
||||
|
||||
// Display displays this terminal in a view
|
||||
func (t *Terminal) Display() {
|
||||
divider := 0
|
||||
if t.view.x != 0 {
|
||||
divider = 1
|
||||
dividerStyle := defStyle
|
||||
if style, ok := colorscheme["divider"]; ok {
|
||||
dividerStyle = style
|
||||
}
|
||||
for i := 0; i < t.view.Height; i++ {
|
||||
screen.SetContent(t.view.x, t.view.y+i, '|', nil, dividerStyle.Reverse(true))
|
||||
}
|
||||
}
|
||||
t.state.Lock()
|
||||
defer t.state.Unlock()
|
||||
|
||||
var l Loc
|
||||
for y := 0; y < t.view.Height; y++ {
|
||||
for x := 0; x < t.view.Width; x++ {
|
||||
l.X, l.Y = x, y
|
||||
c, f, b := t.state.Cell(x, y)
|
||||
|
||||
fg, bg := int(f), int(b)
|
||||
if f == terminal.DefaultFG {
|
||||
fg = int(tcell.ColorDefault)
|
||||
}
|
||||
if b == terminal.DefaultBG {
|
||||
bg = int(tcell.ColorDefault)
|
||||
}
|
||||
st := tcell.StyleDefault.Foreground(GetColor256(int(fg))).Background(GetColor256(int(bg)))
|
||||
|
||||
if l.LessThan(t.selection[1]) && l.GreaterEqual(t.selection[0]) || l.LessThan(t.selection[0]) && l.GreaterEqual(t.selection[1]) {
|
||||
st = st.Reverse(true)
|
||||
}
|
||||
|
||||
screen.SetContent(t.view.x+x+divider, t.view.y+y, c, nil, st)
|
||||
}
|
||||
}
|
||||
if t.state.CursorVisible() && tabs[curTab].CurView == t.view.Num {
|
||||
curx, cury := t.state.Cursor()
|
||||
screen.ShowCursor(curx+t.view.x+divider, cury+t.view.y)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// Util.go is a collection of utility functions that are used throughout
|
||||
@@ -36,7 +36,11 @@ func NumOccurrences(s string, c byte) int {
|
||||
|
||||
// Spaces returns a string with n spaces
|
||||
func Spaces(n int) string {
|
||||
return strings.Repeat(" ", n)
|
||||
var str string
|
||||
for i := 0; i < n; i++ {
|
||||
str += " "
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Min takes the min of two ints
|
||||
@@ -55,7 +59,6 @@ func Max(a, b int) int {
|
||||
return b
|
||||
}
|
||||
|
||||
// FSize gets the size of a file
|
||||
func FSize(f *os.File) int64 {
|
||||
fi, _ := f.Stat()
|
||||
// get the size
|
||||
@@ -246,7 +249,6 @@ func lcs(a, b string) string {
|
||||
return lcs
|
||||
}
|
||||
|
||||
// CommonSubstring gets a common substring among the inputs
|
||||
func CommonSubstring(arr ...string) string {
|
||||
commonStr := arr[0]
|
||||
|
||||
@@ -275,17 +277,93 @@ func ShortFuncName(i interface{}) string {
|
||||
return strings.TrimPrefix(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name(), "main.(*View).")
|
||||
}
|
||||
|
||||
// ReplaceHome takes a path as input and replaces ~ at the start of the path with the user's
|
||||
// home directory. Does nothing if the path does not start with '~'.
|
||||
func ReplaceHome(path string) string {
|
||||
if !strings.HasPrefix(path, "~") {
|
||||
return path
|
||||
// SplitCommandArgs separates multiple command arguments which may be quoted.
|
||||
// The returned slice contains at least one string
|
||||
func SplitCommandArgs(input string) []string {
|
||||
var result []string
|
||||
var curQuote *bytes.Buffer
|
||||
|
||||
curArg := new(bytes.Buffer)
|
||||
escape := false
|
||||
|
||||
finishQuote := func() {
|
||||
if curQuote == nil {
|
||||
return
|
||||
}
|
||||
str := curQuote.String()
|
||||
if unquoted, err := strconv.Unquote(str); err == nil {
|
||||
str = unquoted
|
||||
}
|
||||
curArg.WriteString(str)
|
||||
curQuote = nil
|
||||
}
|
||||
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
messenger.Error("Could not find home directory: ", err)
|
||||
return path
|
||||
appendResult := func() {
|
||||
finishQuote()
|
||||
escape = false
|
||||
|
||||
str := curArg.String()
|
||||
result = append(result, str)
|
||||
curArg.Reset()
|
||||
}
|
||||
return strings.Replace(path, "~", home, 1)
|
||||
|
||||
for _, r := range input {
|
||||
if r == ' ' && curQuote == nil {
|
||||
appendResult()
|
||||
} else {
|
||||
runeHandled := false
|
||||
appendRuneToBuff := func() {
|
||||
if curQuote != nil {
|
||||
curQuote.WriteRune(r)
|
||||
} else {
|
||||
curArg.WriteRune(r)
|
||||
}
|
||||
runeHandled = true
|
||||
}
|
||||
|
||||
if r == '"' && curQuote == nil {
|
||||
curQuote = new(bytes.Buffer)
|
||||
appendRuneToBuff()
|
||||
} else {
|
||||
if curQuote != nil && !escape {
|
||||
if r == '"' {
|
||||
appendRuneToBuff()
|
||||
finishQuote()
|
||||
} else if r == '\\' {
|
||||
appendRuneToBuff()
|
||||
escape = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if !runeHandled {
|
||||
appendRuneToBuff()
|
||||
}
|
||||
}
|
||||
|
||||
escape = false
|
||||
}
|
||||
appendResult()
|
||||
return result
|
||||
}
|
||||
|
||||
// JoinCommandArgs joins multiple command arguments and quote the strings if needed.
|
||||
func JoinCommandArgs(args ...string) string {
|
||||
buf := new(bytes.Buffer)
|
||||
first := true
|
||||
for _, arg := range args {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
buf.WriteRune(' ')
|
||||
}
|
||||
quoted := strconv.Quote(arg)
|
||||
if quoted[1:len(quoted)-1] != arg || strings.ContainsRune(arg, ' ') {
|
||||
buf.WriteString(quoted)
|
||||
} else {
|
||||
buf.WriteString(arg)
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -66,6 +67,56 @@ func TestIsWordChar(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinAndSplitCommandArgs(t *testing.T) {
|
||||
tests := []struct {
|
||||
Query []string
|
||||
Wanted string
|
||||
}{
|
||||
{[]string{`test case`}, `"test case"`},
|
||||
{[]string{`quote "test"`}, `"quote \"test\""`},
|
||||
{[]string{`slash\\\ test`}, `"slash\\\\\\ test"`},
|
||||
{[]string{`path 1`, `path\" 2`}, `"path 1" "path\\\" 2"`},
|
||||
{[]string{`foo`}, `foo`},
|
||||
{[]string{`foo\"bar`}, `"foo\\\"bar"`},
|
||||
{[]string{``}, ``},
|
||||
{[]string{`"`}, `"\""`},
|
||||
{[]string{`a`, ``}, `a `},
|
||||
{[]string{``, ``, ``, ``}, ` `},
|
||||
{[]string{"\n"}, `"\n"`},
|
||||
{[]string{"foo\tbar"}, `"foo\tbar"`},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
if result := JoinCommandArgs(test.Query...); test.Wanted != result {
|
||||
t.Errorf("JoinCommandArgs failed at Test %d\nGot: %q", i, result)
|
||||
}
|
||||
|
||||
if result := SplitCommandArgs(test.Wanted); !reflect.DeepEqual(test.Query, result) {
|
||||
t.Errorf("SplitCommandArgs failed at Test %d\nGot: `%q`", i, result)
|
||||
}
|
||||
}
|
||||
|
||||
splitTests := []struct {
|
||||
Query string
|
||||
Wanted []string
|
||||
}{
|
||||
{`"hallo""Welt"`, []string{`halloWelt`}},
|
||||
{`"hallo" "Welt"`, []string{`hallo`, `Welt`}},
|
||||
{`\"`, []string{`\"`}},
|
||||
{`"foo`, []string{`"foo`}},
|
||||
{`"foo"`, []string{`foo`}},
|
||||
{`"\"`, []string{`"\"`}},
|
||||
{`"C:\\"foo.txt`, []string{`C:\foo.txt`}},
|
||||
{`"\n"new"\n"line`, []string{"\nnew\nline"}},
|
||||
}
|
||||
|
||||
for i, test := range splitTests {
|
||||
if result := SplitCommandArgs(test.Query); !reflect.DeepEqual(test.Wanted, result) {
|
||||
t.Errorf("SplitCommandArgs failed at Split-Test %d\nGot: `%q`", i, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringWidth(t *testing.T) {
|
||||
tabsize := 4
|
||||
if w := StringWidth("1\t2", tabsize); w != 5 {
|
||||
|
||||
1
cmd/micro/vendor/github.com/blang/semver
generated
vendored
1
cmd/micro/vendor/github.com/blang/semver
generated
vendored
Submodule cmd/micro/vendor/github.com/blang/semver deleted from 4a1e882c79
1
cmd/micro/vendor/github.com/dustin/go-humanize
generated
vendored
1
cmd/micro/vendor/github.com/dustin/go-humanize
generated
vendored
Submodule cmd/micro/vendor/github.com/dustin/go-humanize deleted from 259d2a102b
1
cmd/micro/vendor/github.com/flynn/json5
generated
vendored
1
cmd/micro/vendor/github.com/flynn/json5
generated
vendored
Submodule cmd/micro/vendor/github.com/flynn/json5 deleted from 7620272ed6
1
cmd/micro/vendor/github.com/gdamore/encoding
generated
vendored
1
cmd/micro/vendor/github.com/gdamore/encoding
generated
vendored
Submodule cmd/micro/vendor/github.com/gdamore/encoding deleted from b23993cbb6
1
cmd/micro/vendor/github.com/go-errors/errors
generated
vendored
1
cmd/micro/vendor/github.com/go-errors/errors
generated
vendored
Submodule cmd/micro/vendor/github.com/go-errors/errors deleted from 8fa88b06e5
1
cmd/micro/vendor/github.com/lucasb-eyer/go-colorful
generated
vendored
1
cmd/micro/vendor/github.com/lucasb-eyer/go-colorful
generated
vendored
Submodule cmd/micro/vendor/github.com/lucasb-eyer/go-colorful deleted from c900de9dbb
1
cmd/micro/vendor/github.com/mattn/go-isatty
generated
vendored
1
cmd/micro/vendor/github.com/mattn/go-isatty
generated
vendored
Submodule cmd/micro/vendor/github.com/mattn/go-isatty deleted from fc9e8d8ef4
1
cmd/micro/vendor/github.com/mattn/go-runewidth
generated
vendored
1
cmd/micro/vendor/github.com/mattn/go-runewidth
generated
vendored
Submodule cmd/micro/vendor/github.com/mattn/go-runewidth deleted from 97311d9f77
1
cmd/micro/vendor/github.com/mitchellh/go-homedir
generated
vendored
1
cmd/micro/vendor/github.com/mitchellh/go-homedir
generated
vendored
Submodule cmd/micro/vendor/github.com/mitchellh/go-homedir deleted from b8bc1bf767
1
cmd/micro/vendor/github.com/sergi/go-diff
generated
vendored
1
cmd/micro/vendor/github.com/sergi/go-diff
generated
vendored
Submodule cmd/micro/vendor/github.com/sergi/go-diff deleted from feef008d51
1
cmd/micro/vendor/github.com/yuin/gopher-lua
generated
vendored
1
cmd/micro/vendor/github.com/yuin/gopher-lua
generated
vendored
Submodule cmd/micro/vendor/github.com/yuin/gopher-lua deleted from b402f3114e
1
cmd/micro/vendor/github.com/zyedidia/clipboard
generated
vendored
1
cmd/micro/vendor/github.com/zyedidia/clipboard
generated
vendored
Submodule cmd/micro/vendor/github.com/zyedidia/clipboard deleted from adacf416ce
1
cmd/micro/vendor/github.com/zyedidia/glob
generated
vendored
1
cmd/micro/vendor/github.com/zyedidia/glob
generated
vendored
Submodule cmd/micro/vendor/github.com/zyedidia/glob deleted from dd4023a66d
1
cmd/micro/vendor/github.com/zyedidia/poller
generated
vendored
1
cmd/micro/vendor/github.com/zyedidia/poller
generated
vendored
Submodule cmd/micro/vendor/github.com/zyedidia/poller deleted from ab09682913
1
cmd/micro/vendor/github.com/zyedidia/pty
generated
vendored
1
cmd/micro/vendor/github.com/zyedidia/pty
generated
vendored
Submodule cmd/micro/vendor/github.com/zyedidia/pty deleted from 30364665a2
1
cmd/micro/vendor/github.com/zyedidia/tcell
generated
vendored
1
cmd/micro/vendor/github.com/zyedidia/tcell
generated
vendored
Submodule cmd/micro/vendor/github.com/zyedidia/tcell deleted from 208b6e8f2f
1
cmd/micro/vendor/github.com/zyedidia/terminal
generated
vendored
1
cmd/micro/vendor/github.com/zyedidia/terminal
generated
vendored
Submodule cmd/micro/vendor/github.com/zyedidia/terminal deleted from 1760577dbc
1
cmd/micro/vendor/golang.org/x/net
generated
vendored
1
cmd/micro/vendor/golang.org/x/net
generated
vendored
Submodule cmd/micro/vendor/golang.org/x/net deleted from 1a68b1313c
1
cmd/micro/vendor/golang.org/x/text
generated
vendored
1
cmd/micro/vendor/golang.org/x/text
generated
vendored
Submodule cmd/micro/vendor/golang.org/x/text deleted from 210eee5cf7
1
cmd/micro/vendor/gopkg.in/yaml.v2
generated
vendored
1
cmd/micro/vendor/gopkg.in/yaml.v2
generated
vendored
Submodule cmd/micro/vendor/gopkg.in/yaml.v2 deleted from cd8b52f826
1
cmd/micro/vendor/layeh.com/gopher-luar
generated
vendored
1
cmd/micro/vendor/layeh.com/gopher-luar
generated
vendored
Submodule cmd/micro/vendor/layeh.com/gopher-luar deleted from 16281577df
@@ -1,21 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/zyedidia/tcell"
|
||||
)
|
||||
|
||||
// The ViewType defines what kind of view this is
|
||||
type ViewType struct {
|
||||
Kind int
|
||||
Readonly bool // The file cannot be edited
|
||||
Scratch bool // The file cannot be saved
|
||||
kind int
|
||||
readonly bool // The file cannot be edited
|
||||
scratch bool // The file cannot be saved
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -23,8 +21,6 @@ var (
|
||||
vtHelp = ViewType{1, true, true}
|
||||
vtLog = ViewType{2, true, true}
|
||||
vtScratch = ViewType{3, false, true}
|
||||
vtRaw = ViewType{4, true, true}
|
||||
vtTerm = ViewType{5, true, true}
|
||||
)
|
||||
|
||||
// The View struct stores information about a view into a buffer.
|
||||
@@ -66,7 +62,7 @@ type View struct {
|
||||
// The buffer
|
||||
Buf *Buffer
|
||||
// The statusline
|
||||
sline *Statusline
|
||||
sline Statusline
|
||||
|
||||
// Since tcell doesn't differentiate between a mouse release event
|
||||
// and a mouse move event with no keys pressed, we need to keep
|
||||
@@ -74,12 +70,9 @@ type View struct {
|
||||
// mouse release events
|
||||
mouseReleased bool
|
||||
|
||||
// We need to keep track of insert key press toggle
|
||||
isOverwriteMode bool
|
||||
// This stores when the last click was
|
||||
// This is useful for detecting double and triple clicks
|
||||
lastClickTime time.Time
|
||||
lastLoc Loc
|
||||
|
||||
// lastCutTime stores when the last ctrl+k was issued.
|
||||
// It is used for clearing the clipboard to replace it with fresh cut lines.
|
||||
@@ -95,16 +88,9 @@ type View struct {
|
||||
// Same here, just to keep track for mouse move events
|
||||
tripleClick bool
|
||||
|
||||
// The cellview used for displaying and syntax highlighting
|
||||
cellview *CellView
|
||||
|
||||
splitNode *LeafNode
|
||||
|
||||
// The scrollbar
|
||||
scrollbar *ScrollBar
|
||||
|
||||
// Virtual terminal
|
||||
term *Terminal
|
||||
}
|
||||
|
||||
// NewView returns a new fullscreen view
|
||||
@@ -130,11 +116,7 @@ func NewViewWidthHeight(buf *Buffer, w, h int) *View {
|
||||
|
||||
v.messages = make(map[string][]GutterMessage)
|
||||
|
||||
v.sline = &Statusline{
|
||||
view: v,
|
||||
}
|
||||
|
||||
v.scrollbar = &ScrollBar{
|
||||
v.sline = Statusline{
|
||||
view: v,
|
||||
}
|
||||
|
||||
@@ -142,8 +124,6 @@ func NewViewWidthHeight(buf *Buffer, w, h int) *View {
|
||||
v.Height--
|
||||
}
|
||||
|
||||
v.term = new(Terminal)
|
||||
|
||||
for pl := range loadedPlugins {
|
||||
_, err := Call(pl+".onViewOpen", v)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
|
||||
@@ -164,24 +144,6 @@ func (v *View) ToggleStatusLine() {
|
||||
}
|
||||
}
|
||||
|
||||
// StartTerminal execs a command in this view
|
||||
func (v *View) StartTerminal(execCmd []string, wait bool, getOutput bool, luaCallback string) error {
|
||||
err := v.term.Start(execCmd, v, getOutput)
|
||||
v.term.wait = wait
|
||||
v.term.callback = luaCallback
|
||||
if err == nil {
|
||||
v.term.Resize(v.Width, v.Height)
|
||||
v.Type = vtTerm
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// CloseTerminal shuts down the tty running in this view
|
||||
// and returns it to the default view type
|
||||
func (v *View) CloseTerminal() {
|
||||
v.term.Stop()
|
||||
}
|
||||
|
||||
// ToggleTabbar creates an extra row for the tabbar if necessary
|
||||
func (v *View) ToggleTabbar() {
|
||||
if len(tabs) > 1 {
|
||||
@@ -207,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")
|
||||
}
|
||||
@@ -236,7 +198,7 @@ func (v *View) ScrollDown(n int) {
|
||||
// If there are unsaved changes, the user will be asked if the view can be closed
|
||||
// causing them to lose the unsaved changes
|
||||
func (v *View) CanClose() bool {
|
||||
if v.Type == vtDefault && v.Buf.Modified() {
|
||||
if v.Type == vtDefault && v.Buf.IsModified {
|
||||
var choice bool
|
||||
var canceled bool
|
||||
if v.Buf.Settings["autosave"].(bool) {
|
||||
@@ -248,12 +210,15 @@ func (v *View) CanClose() bool {
|
||||
//if char == 'y' {
|
||||
if choice {
|
||||
v.Save(true)
|
||||
return true
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
// OpenBuffer opens a new buffer in this view.
|
||||
@@ -273,18 +238,13 @@ func (v *View) OpenBuffer(buf *Buffer) {
|
||||
// Set mouseReleased to true because we assume the mouse is not being pressed when
|
||||
// the editor is opened
|
||||
v.mouseReleased = true
|
||||
// Set isOverwriteMode to false, because we assume we are in the default mode when editor
|
||||
// is opened
|
||||
v.isOverwriteMode = false
|
||||
v.lastClickTime = time.Time{}
|
||||
|
||||
GlobalPluginCall("onBufferOpen", v.Buf)
|
||||
GlobalPluginCall("onViewOpen", v)
|
||||
}
|
||||
|
||||
// Open opens the given file in the view
|
||||
func (v *View) Open(filename string) {
|
||||
filename = ReplaceHome(filename)
|
||||
home, _ := homedir.Dir()
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
file, err := os.Open(filename)
|
||||
fileInfo, _ := os.Stat(filename)
|
||||
|
||||
@@ -325,7 +285,7 @@ func (v *View) ReOpen() {
|
||||
// HSplit opens a horizontal split with the given buffer
|
||||
func (v *View) HSplit(buf *Buffer) {
|
||||
i := 0
|
||||
if v.Buf.Settings["splitbottom"].(bool) {
|
||||
if v.Buf.Settings["splitBottom"].(bool) {
|
||||
i = 1
|
||||
}
|
||||
v.splitNode.HSplit(buf, v.Num+i)
|
||||
@@ -334,7 +294,7 @@ func (v *View) HSplit(buf *Buffer) {
|
||||
// VSplit opens a vertical split with the given buffer
|
||||
func (v *View) VSplit(buf *Buffer) {
|
||||
i := 0
|
||||
if v.Buf.Settings["splitright"].(bool) {
|
||||
if v.Buf.Settings["splitRight"].(bool) {
|
||||
i = 1
|
||||
}
|
||||
v.splitNode.VSplit(buf, v.Num+i)
|
||||
@@ -395,10 +355,6 @@ func (v *View) GetSoftWrapLocation(vx, vy int) (int, int) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// Bottomline returns the line number of the lowest line in the view
|
||||
// You might think that this is obviously just v.Topline + v.Height
|
||||
// but if softwrap is enabled things get complicated since one buffer
|
||||
// line can take up multiple lines in the view
|
||||
func (v *View) Bottomline() int {
|
||||
if !v.Buf.Settings["softwrap"].(bool) {
|
||||
return v.Topline + v.Height
|
||||
@@ -470,31 +426,6 @@ func (v *View) Relocate() bool {
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetMouseClickLocation gets the location in the buffer from a mouse click
|
||||
// on the screen
|
||||
func (v *View) GetMouseClickLocation(x, y int) (int, int) {
|
||||
x -= v.lineNumOffset - v.leftCol + v.x
|
||||
y += v.Topline - v.y
|
||||
|
||||
if y-v.Topline > v.Height-1 {
|
||||
v.ScrollDown(1)
|
||||
y = v.Height + v.Topline - 1
|
||||
}
|
||||
if y < 0 {
|
||||
y = 0
|
||||
}
|
||||
if x < 0 {
|
||||
x = 0
|
||||
}
|
||||
|
||||
newX, newY := v.GetSoftWrapLocation(x, y)
|
||||
if newX > Count(v.Buf.Line(newY)) {
|
||||
newX = Count(v.Buf.Line(newY))
|
||||
}
|
||||
|
||||
return newX, newY
|
||||
}
|
||||
|
||||
// MoveToMouseClick moves the cursor to location x, y assuming x, y were given
|
||||
// by a mouse click
|
||||
func (v *View) MoveToMouseClick(x, y int) {
|
||||
@@ -510,6 +441,7 @@ func (v *View) MoveToMouseClick(x, y int) {
|
||||
}
|
||||
|
||||
x, y = v.GetSoftWrapLocation(x, y)
|
||||
// x = v.Cursor.GetCharPosInLine(y, x)
|
||||
if x > Count(v.Buf.Line(y)) {
|
||||
x = Count(v.Buf.Line(y))
|
||||
}
|
||||
@@ -518,68 +450,8 @@ func (v *View) MoveToMouseClick(x, y int) {
|
||||
v.Cursor.LastVisualX = v.Cursor.GetVisualX()
|
||||
}
|
||||
|
||||
// Execute actions executes the supplied actions
|
||||
func (v *View) ExecuteActions(actions []func(*View, bool) bool) bool {
|
||||
relocate := false
|
||||
readonlyBindingsList := []string{"Delete", "Insert", "Backspace", "Cut", "Play", "Paste", "Move", "Add", "DuplicateLine", "Macro"}
|
||||
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
|
||||
}
|
||||
|
||||
// SetCursor sets the view's and buffer's cursor
|
||||
func (v *View) SetCursor(c *Cursor) bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
v.Cursor = c
|
||||
v.Buf.curCursor = c.Num
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// HandleEvent handles an event passed by the main loop
|
||||
func (v *View) HandleEvent(event tcell.Event) {
|
||||
if v.Type == vtTerm {
|
||||
v.term.HandleEvent(event)
|
||||
return
|
||||
}
|
||||
|
||||
if v.Type == vtRaw {
|
||||
v.Buf.Insert(v.Cursor.Loc, reflect.TypeOf(event).String()[7:])
|
||||
v.Buf.Insert(v.Cursor.Loc, fmt.Sprintf(": %q\n", event.EscSeq()))
|
||||
|
||||
switch e := event.(type) {
|
||||
case *tcell.EventKey:
|
||||
if e.Key() == tcell.KeyCtrlQ {
|
||||
v.Quit(true)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// This bool determines whether the view is relocated at the end of the function
|
||||
// By default it's true because most events should cause a relocate
|
||||
relocate := true
|
||||
@@ -587,138 +459,149 @@ func (v *View) HandleEvent(event tcell.Event) {
|
||||
v.Buf.CheckModTime()
|
||||
|
||||
switch e := event.(type) {
|
||||
case *tcell.EventRaw:
|
||||
for key, actions := range bindings {
|
||||
if key.keyCode == -1 {
|
||||
if e.EscSeq() == key.escape {
|
||||
for _, c := range v.Buf.cursors {
|
||||
ok := v.SetCursor(c)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
relocate = false
|
||||
relocate = v.ExecuteActions(actions) || relocate
|
||||
}
|
||||
v.SetCursor(&v.Buf.Cursor)
|
||||
v.Buf.MergeCursors()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
case *tcell.EventKey:
|
||||
// Check first if input is a key binding, if it is we 'eat' the input and don't insert a rune
|
||||
isBinding := false
|
||||
for key, actions := range bindings {
|
||||
if e.Key() == key.keyCode {
|
||||
if e.Key() == tcell.KeyRune {
|
||||
if e.Rune() != key.r {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if e.Modifiers() == key.modifiers {
|
||||
for _, c := range v.Buf.cursors {
|
||||
ok := v.SetCursor(c)
|
||||
if !ok {
|
||||
break
|
||||
readonlyBindingsList := []string{"Delete", "Insert", "Backspace", "Cut", "Play", "Paste", "Move", "Add", "DuplicateLine", "Macro"}
|
||||
if e.Key() != tcell.KeyRune || e.Modifiers() != 0 {
|
||||
for key, actions := range bindings {
|
||||
if e.Key() == key.keyCode {
|
||||
if e.Key() == tcell.KeyRune {
|
||||
if e.Rune() != key.r {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if e.Modifiers() == key.modifiers {
|
||||
relocate = false
|
||||
isBinding = true
|
||||
relocate = v.ExecuteActions(actions) || relocate
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
v.SetCursor(&v.Buf.Cursor)
|
||||
v.Buf.MergeCursors()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !isBinding && e.Key() == tcell.KeyRune {
|
||||
// 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)
|
||||
if v.Type.readonly == false {
|
||||
// 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()
|
||||
|
||||
// Insert a character
|
||||
if v.Cursor.HasSelection() {
|
||||
v.Cursor.DeleteSelection()
|
||||
v.Cursor.ResetSelection()
|
||||
}
|
||||
|
||||
if v.isOverwriteMode {
|
||||
next := v.Cursor.Loc
|
||||
next.X++
|
||||
v.Buf.Replace(v.Cursor.Loc, next, string(e.Rune()))
|
||||
} else {
|
||||
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())
|
||||
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)
|
||||
}
|
||||
}
|
||||
v.SetCursor(&v.Buf.Cursor)
|
||||
|
||||
if recordingMacro {
|
||||
curMacro = append(curMacro, e.Rune())
|
||||
}
|
||||
}
|
||||
}
|
||||
case *tcell.EventPaste:
|
||||
// Check viewtype if readonly don't paste (readonly help and log view etc.)
|
||||
if v.Type.Readonly == false {
|
||||
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)
|
||||
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()
|
||||
|
||||
for key, actions := range bindings {
|
||||
if button == key.buttons && e.Modifiers() == key.modifiers {
|
||||
for _, c := range v.Buf.cursors {
|
||||
ok := v.SetCursor(c)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
relocate = v.ExecuteActions(actions) || relocate
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
case tcell.Button2:
|
||||
// Check viewtype if readonly don't paste (readonly help and log view etc.)
|
||||
if v.Type.readonly == false {
|
||||
// Middle mouse button was clicked,
|
||||
// We should paste primary
|
||||
v.PastePrimary(true)
|
||||
}
|
||||
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
|
||||
@@ -732,6 +615,14 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -746,10 +637,6 @@ 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--
|
||||
@@ -798,19 +685,13 @@ func (v *View) openHelp(helpPage string) {
|
||||
}
|
||||
}
|
||||
|
||||
// DisplayView draws the view to the screen
|
||||
func (v *View) DisplayView() {
|
||||
if v.Type == vtTerm {
|
||||
v.term.Display()
|
||||
return
|
||||
}
|
||||
|
||||
if v.Buf.Settings["softwrap"].(bool) && v.leftCol != 0 {
|
||||
v.leftCol = 0
|
||||
}
|
||||
|
||||
if v.Type == vtLog || v.Type == vtRaw {
|
||||
// Log or raw views should always follow the cursor...
|
||||
if v.Type == vtLog {
|
||||
// Log views should always follow the cursor...
|
||||
v.Relocate()
|
||||
}
|
||||
|
||||
@@ -874,11 +755,11 @@ func (v *View) DisplayView() {
|
||||
}
|
||||
|
||||
colorcolumn := int(v.Buf.Settings["colorcolumn"].(float64))
|
||||
if colorcolumn != 0 && xOffset+colorcolumn-v.leftCol < v.Width {
|
||||
if colorcolumn != 0 {
|
||||
style := GetColor("color-column")
|
||||
fg, _, _ := style.Decompose()
|
||||
st := defStyle.Background(fg)
|
||||
screen.SetContent(xOffset+colorcolumn-v.leftCol, yOffset+visualLineN, ' ', nil, st)
|
||||
screen.SetContent(xOffset+colorcolumn, yOffset+visualLineN, ' ', nil, st)
|
||||
}
|
||||
|
||||
screenX = v.x
|
||||
@@ -982,20 +863,22 @@ func (v *View) DisplayView() {
|
||||
}
|
||||
|
||||
charLoc := char.realLoc
|
||||
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 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
|
||||
}
|
||||
}
|
||||
v.SetCursor(&v.Buf.Cursor)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if v.Buf.Settings["cursorline"].(bool) && tabs[curTab].CurView == v.Num &&
|
||||
!v.Cursor.HasSelection() && v.Cursor.Y == realLineN {
|
||||
@@ -1006,16 +889,6 @@ 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
|
||||
}
|
||||
}
|
||||
@@ -1026,27 +899,19 @@ func (v *View) DisplayView() {
|
||||
var cx, cy int
|
||||
if lastChar != nil {
|
||||
lastX = xOffset + lastChar.visualLoc.X + lastChar.width
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
v.SetCursor(&v.Buf.Cursor)
|
||||
realLoc = Loc{lastChar.realLoc.X + 1, realLineN}
|
||||
realLoc = Loc{lastChar.realLoc.X, realLineN}
|
||||
visualLoc = Loc{lastX - xOffset, lastChar.visualLoc.Y}
|
||||
} else if len(line) == 0 {
|
||||
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
|
||||
}
|
||||
if tabs[curTab].CurView == v.Num && !v.Cursor.HasSelection() &&
|
||||
v.Cursor.Y == realLineN {
|
||||
screen.ShowCursor(xOffset, yOffset+visualLineN)
|
||||
cx, cy = xOffset, yOffset+visualLineN
|
||||
}
|
||||
v.SetCursor(&v.Buf.Cursor)
|
||||
lastX = xOffset
|
||||
realLoc = Loc{0, realLineN}
|
||||
visualLoc = Loc{0, visualLineN}
|
||||
@@ -1066,7 +931,7 @@ func (v *View) DisplayView() {
|
||||
|
||||
if v.Buf.Settings["cursorline"].(bool) && tabs[curTab].CurView == v.Num &&
|
||||
!v.Cursor.HasSelection() && v.Cursor.Y == realLineN {
|
||||
for i := lastX; i < xOffset+v.Width-v.lineNumOffset; i++ {
|
||||
for i := lastX; i < xOffset+v.Width; i++ {
|
||||
style := GetColor("cursor-line")
|
||||
fg, _, _ := style.Decompose()
|
||||
style = style.Background(fg)
|
||||
@@ -1088,18 +953,6 @@ 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) {
|
||||
@@ -1111,11 +964,6 @@ func (v *View) Display() {
|
||||
screen.HideCursor()
|
||||
}
|
||||
_, screenH := screen.Size()
|
||||
|
||||
if v.Buf.Settings["scrollbar"].(bool) {
|
||||
v.scrollbar.Display()
|
||||
}
|
||||
|
||||
if v.Buf.Settings["statusline"].(bool) {
|
||||
v.sline.Display()
|
||||
} else if (v.y + v.Height) != screenH-1 {
|
||||
|
||||
@@ -1,23 +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>
|
||||
<metadata_license>MIT</metadata_license>
|
||||
<categories>
|
||||
<category>Development</category>
|
||||
<category>TextEditor</category>
|
||||
</categories>
|
||||
<provides>
|
||||
<binary>micro</binary>
|
||||
</provides>
|
||||
<developer_name>Zachary Yedidia</developer_name>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<caption>Micro Text Editor editing its source code.</caption>
|
||||
<image type="source">https://raw.githubusercontent.com/zyedidia/micro/master/assets/micro-solarized.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<url type="homepage">https://micro-editor.github.io</url>
|
||||
<url type="bugtracker">https://github.com/zyedidia/micro/issues</url>
|
||||
<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>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
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"
|
||||
@@ -1,3 +1,4 @@
|
||||
# This is the monokai colorscheme
|
||||
color-link default "#F8F8F2,#282828"
|
||||
color-link comment "#75715E,#282828"
|
||||
color-link identifier "#66D9EF,#282828"
|
||||
@@ -13,7 +14,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"
|
||||
@@ -21,7 +22,6 @@ 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.tag "#AE81FF,#282828"
|
||||
color-link symbol.brackets "default"
|
||||
|
||||
30
runtime/colorschemes/flamepoint-tc.micro
Normal file
30
runtime/colorschemes/flamepoint-tc.micro
Normal file
@@ -0,0 +1,30 @@
|
||||
#Flamepoint theme
|
||||
#By CaptainMcClellan
|
||||
color-link default ""
|
||||
color-link comment ""
|
||||
color-link constant ""
|
||||
color-link constant.bool ""
|
||||
color-link constant.bool.true ""
|
||||
color-link constant.bool.false ""
|
||||
color-link constant.number ""
|
||||
color-link constant.specialChar ""
|
||||
color-link constant.string ""
|
||||
color-link constant.string.url "underline"
|
||||
color-link identifier ""
|
||||
color-link identifier.var ""
|
||||
color-link preproc ""
|
||||
color-link special ""
|
||||
color-link statement ""
|
||||
color-link symbol ""
|
||||
color-link symbol.brackets ""
|
||||
color-link symbol.tag ""
|
||||
color-link type ""
|
||||
color-link type.keyword ""
|
||||
color-link error ""
|
||||
color-link todo ""
|
||||
color-link cursor-line ""
|
||||
color-link statusline ""
|
||||
color-link tabbar ""
|
||||
color-link color-column ""
|
||||
color-link gutter-error ""
|
||||
color-link gutter-warning ""
|
||||
1
runtime/colorschemes/funky-cactus-tc.micro
Normal file
1
runtime/colorschemes/funky-cactus-tc.micro
Normal file
@@ -0,0 +1 @@
|
||||
#Funky Cactus theme in true colour.
|
||||
30
runtime/colorschemes/nes-tc.micro
Normal file
30
runtime/colorschemes/nes-tc.micro
Normal file
@@ -0,0 +1,30 @@
|
||||
#NES
|
||||
#A color theme only using NES pallette colours
|
||||
color-link default ""
|
||||
color-link comment ""
|
||||
color-link constant ""
|
||||
color-link constant.bool ""
|
||||
color-link constant.bool.true ""
|
||||
color-link constant.bool.false ""
|
||||
color-link constant.number ""
|
||||
color-link constant.specialChar ""
|
||||
color-link constant.string ""
|
||||
color-link constant.string.url "underline"
|
||||
color-link identifier ""
|
||||
color-link identifier.var ""
|
||||
color-link preproc ""
|
||||
color-link special ""
|
||||
color-link statement ""
|
||||
color-link symbol ""
|
||||
color-link symbol.brackets ""
|
||||
color-link symbol.tag ""
|
||||
color-link type ""
|
||||
color-link type.keyword ""
|
||||
color-link error ""
|
||||
color-link todo ""
|
||||
color-link cursor-line ""
|
||||
color-link statusline ""
|
||||
color-link tabbar ""
|
||||
color-link color-column ""
|
||||
color-link gutter-error ""
|
||||
color-link gutter-warning ""
|
||||
@@ -1,26 +0,0 @@
|
||||
color-link default "#e6e1dc,#2b2b2b"
|
||||
color-link comment "#bc9458,#2b2b2b"
|
||||
color-link statement "#cc7833,#2b2b2b"
|
||||
color-link constant "#a5c261,#2b2b2b"
|
||||
color-link constant.bool "#6d9cbe,#2b2b2b"
|
||||
color-link type "#6d9cbe,#2b2b2b"
|
||||
color-link preproc "#cc7833,#2b2b2b"
|
||||
color-link special "#cc7833,#2b2b2b"
|
||||
color-link underlined "#cc7833,#2b2b2b"
|
||||
color-link todo "bold #cc7833,#2b2b2b"
|
||||
color-link error "bold #cc7833,#2b2b2b"
|
||||
color-link gutter-error "#cc7833,#11151C"
|
||||
color-link indent-char "#414141,#2b2b2b"
|
||||
color-link line-number "#a1a1a1,#353535"
|
||||
color-link current-line-number "#e6e1dc,#2b2b2b"
|
||||
color-link gutter-warning "#a5c261,#11151C"
|
||||
color-link symbol "#edb753,#2b2b2b"
|
||||
color-link identifier "#edb753,#2b2b2b"
|
||||
color-link statusline "#a1a1a1,#414141"
|
||||
color-link tabbar "bold #a1a1a1,#414141"
|
||||
color-link cursor-line "#353535"
|
||||
color-link color-column "#353535"
|
||||
color-link space "underline #e6e1dc,#2b2b2b"
|
||||
|
||||
#the Python syntax definition are wrong. This is not how you should do decorators!
|
||||
color-link brightgreen "#edb753,#2b2b2b"
|
||||
@@ -1,32 +0,0 @@
|
||||
# Twilight color scheme
|
||||
color-link default "#F8F8F8,#141414"
|
||||
color-link color-column "#1B1B1B"
|
||||
color-link comment "#5F5A60"
|
||||
color-link constant "#CF6A4C"
|
||||
#color-link constant.number "#CF6A4C"
|
||||
color-link constant.specialChar "#DDF2A4"
|
||||
color-link constant.string "#8F9D6A"
|
||||
color-link current-line-number "#868686,#1B1B1B"
|
||||
color-link cursor-line "#1B1B1B"
|
||||
color-link divider "#1E1E1E"
|
||||
color-link error "#D2A8A1"
|
||||
color-link gutter-error "#9B859D"
|
||||
color-link gutter-warning "#9B859D"
|
||||
color-link identifier "#9B703F"
|
||||
color-link identifier.class "#DAD085"
|
||||
color-link identifier.var "#7587A6"
|
||||
color-link indent-char "#515151"
|
||||
color-link line-number "#868686"
|
||||
color-link preproc "#E0C589"
|
||||
color-link special "#E0C589"
|
||||
color-link statement "#CDA869"
|
||||
color-link statusline "#515151,#1E1E1E"
|
||||
color-link symbol "#AC885B"
|
||||
color-link symbol.brackets "#F8F8F8"
|
||||
color-link symbol.operator "#CDA869"
|
||||
color-link symbol.tag "#AC885B"
|
||||
color-link tabbar "#F2F0EC,#2D2D2D"
|
||||
color-link todo "#8B98AB"
|
||||
color-link type "#F9EE98"
|
||||
color-link type.keyword "#CDA869"
|
||||
color-link underlined "#8996A8"
|
||||
@@ -5,69 +5,79 @@ This help page aims to cover two aspects of micro's syntax highlighting engine:
|
||||
- How to create colorschemes and use them
|
||||
- How to create syntax files to add to the list of languages micro can highlight
|
||||
|
||||
|
||||
## Colorschemes
|
||||
|
||||
Micro comes with a number of colorschemes by default. Here is the list:
|
||||
|
||||
### 256 color
|
||||
* simple: this is the simplest colorscheme. It uses 16 colors which are
|
||||
set by your terminal
|
||||
|
||||
These should work and look nice in most terminals. I recommend these
|
||||
themes the most.
|
||||
* mc: A 16-color theme based on the look and feel of GNU Midnight Commander.
|
||||
This will look great used in conjunction with Midnight Commander.
|
||||
|
||||
* nano: A 16-color theme loosely based on GNU nano's syntax highlighting.
|
||||
|
||||
* monokai: this is the monokai colorscheme; you may recognize it as
|
||||
Sublime Text's default colorscheme. It requires true color to
|
||||
look perfect, but the 256 color approximation looks very good as well.
|
||||
It's also the default colorscheme.
|
||||
|
||||
* `monokai`: this is the monokai colorscheme; you may recognize it as Sublime
|
||||
Text's default colorscheme. It requires true color to look perfect, but the
|
||||
256 color approximation looks very good as well. It's also the default
|
||||
colorscheme.
|
||||
* `zenburn`
|
||||
* `gruvbox`
|
||||
* `darcula`
|
||||
* `twilight`
|
||||
* `railscast`
|
||||
* `bubblegum`: a light colorscheme
|
||||
* zenburn: The 'zenburn' colorscheme and works well with 256 color terminals
|
||||
|
||||
### 16 color
|
||||
* solarized: this is the solarized colorscheme.
|
||||
You should have the solarized color palette in your terminal to use it.
|
||||
|
||||
These may vary widely based on the 16 colors selected for your terminal.
|
||||
* solarized-tc: this is the solarized colorscheme for true color; just
|
||||
make sure your terminal supports true color before using it and that the
|
||||
MICRO_TRUECOLOR environment variable is set to 1 before starting micro.
|
||||
|
||||
* `simple`: this is the simplest colorscheme. It uses 16 colors which are set by
|
||||
your terminal
|
||||
* `solarized`: You should have the solarized color palette in your terminal to use this colorscheme properly.
|
||||
* `cmc-16`
|
||||
* `cmc-paper`: cmc-16, but on a white background. (Actually light grey
|
||||
on most ANSI (16-color) terminals)
|
||||
* `geany`: Colorscheme based on geany's default highlighting.
|
||||
* atom-dark-tc: this colorscheme is based off of Atom's "dark" colorscheme.
|
||||
It requires true color to look good.
|
||||
|
||||
### True color
|
||||
* cmc-16: A very nice 16-color theme. Written by contributor CaptainMcClellan
|
||||
(Collin Warren.) Licensed under the same license as the rest of the themes.
|
||||
|
||||
These require terminals that support true color and require `MICRO_TRUECOLOR=1` (this is an environment variable).
|
||||
* cmc-paper: Basically cmc-16, but on a white background. ( Actually light grey on most
|
||||
ANSI (16-color) terminals.)
|
||||
|
||||
* `solarized-tc`: this is the solarized colorscheme for true color.
|
||||
* `atom-dark-tc`: this colorscheme is based off of Atom's "dark" colorscheme.
|
||||
* `cmc-tc`: A true colour variant of the cmc theme. It requires true color to
|
||||
look its best. Use cmc-16 if your terminal doesn't support true color.
|
||||
* `gruvbox-tc`: The true color version of the gruvbox colorscheme
|
||||
* `github-tc`: The true color version of the Github colorscheme
|
||||
* cmc-tc: A true colour variant of the cmc theme.
|
||||
It requires true color to look its best. Use cmc-16 if your terminal doesn't support true color.
|
||||
|
||||
To enable one of these colorschemes just press CtrlE in micro and type
|
||||
`set colorscheme solarized`. (or whichever one you choose). You can also use
|
||||
`set colorscheme monochrome` if you'd prefer to have just the terminal's default
|
||||
foreground and background colors. Note: This provides no syntax highlighting!
|
||||
* codeblocks: A colorscheme based on the Code::Blocks IDE's default syntax highlighting.
|
||||
|
||||
* codeblocks-paper: Same as codeblocks, but on a white background. ( Actually light grey. )
|
||||
|
||||
* github-tc: A colorscheme based on Github's syntax highlighting. Requires true color to look its best.
|
||||
|
||||
* paper-tc: A nice minimalist theme with a light background, good for editing documents on.
|
||||
Requires true color to look its best. Not to be confused with `-paper` suffixed themes.
|
||||
|
||||
* geany: Colorscheme based on geany's default highlighting.
|
||||
|
||||
* geany-alt-tc: Based on an alternate theme bundled with geany.
|
||||
|
||||
* flamepoint-tc: A fire inspired, high intensity true color theme written by CaptainMcClellan.
|
||||
As with all the other `-tc` suffixed themes, it looks its best on a
|
||||
|
||||
To enable one of these colorschemes just press CtrlE in micro and type `set colorscheme solarized`.
|
||||
(or whichever one you choose). You can also use `set colorscheme monochrome` if you'd prefer
|
||||
to have just the terminal's default foreground and background colors.
|
||||
Note: This provides no syntax highlighting!
|
||||
|
||||
See `help gimmickcolors` for a list of some true colour themes that are more
|
||||
just for fun than for serious use. (Though feel free if you want!)
|
||||
just for fun than for serious use. ( Though feel free if you want! )
|
||||
|
||||
---
|
||||
|
||||
## Creating a Colorscheme
|
||||
### Creating a Colorscheme
|
||||
|
||||
Micro's colorschemes are also extremely simple to create. The default ones ca
|
||||
be found
|
||||
Micro's colorschemes are also extremely simple to create. The default ones can be found
|
||||
[here](https://github.com/zyedidia/micro/tree/master/runtime/colorschemes).
|
||||
|
||||
They are only about 18-30 lines in total.
|
||||
|
||||
Basically to create the colorscheme you need to link highlight groups with
|
||||
actual colors. This is done using the `color-link` command.
|
||||
Basically to create the colorscheme you need to link highlight groups with actual colors.
|
||||
This is done using the `color-link` command.
|
||||
|
||||
For example, to highlight all comments in green, you would use the command:
|
||||
|
||||
@@ -99,22 +109,20 @@ color-link comment "bold red"
|
||||
|
||||
There are three different ways to specify the color.
|
||||
|
||||
Color terminals usually have 16 colors that are preset by the user. This means
|
||||
that you cannot depend on those colors always being the same. You can use those
|
||||
colors with the names `black, red, green, yellow, blue, magenta, cyan, white`
|
||||
and the bright variants of each one (brightblack, brightred...).
|
||||
Color terminals usually have 16 colors that are preset by the user. This means that
|
||||
you cannot depend on those colors always being the same. You can use those colors with
|
||||
the names `black, red, green, yellow, blue, magenta, cyan, white` and the bright variants
|
||||
of each one (brightblack, brightred...).
|
||||
|
||||
Then you can use the terminals 256 colors by using their numbers 1-256 (numbers
|
||||
1-16 will refer to the named colors).
|
||||
Then you can use the terminals 256 colors by using their numbers 1-256 (numbers 1-16 will
|
||||
refer to the named colors).
|
||||
|
||||
If the user's terminal supports true color, then you can also specify colors
|
||||
exactly using their hex codes. If the terminal is not true color but micro is
|
||||
told to use a true color colorscheme it will attempt to map the colors to the
|
||||
available 256 colors.
|
||||
If the user's terminal supports true color, then you can also specify colors exactly using
|
||||
their hex codes. If the terminal is not true color but micro is told to use a true color colorscheme
|
||||
it will attempt to map the colors to the available 256 colors.
|
||||
|
||||
Generally colorschemes which require true color terminals to look good are
|
||||
marked with a `-tc` suffix and colorschemes which supply a white background are
|
||||
marked with a `-paper` suffix.
|
||||
Generally colorschemes which require true color terminals to look good are marked with a `-tc` suffix
|
||||
and colorschemes which supply a white background are marked with a `-paper` suffix.
|
||||
|
||||
---
|
||||
|
||||
@@ -132,10 +140,9 @@ Here is a list of the colorscheme groups that you can use:
|
||||
* underlined
|
||||
* error
|
||||
* todo
|
||||
* statusline (Color of the statusline)
|
||||
* tabbar (Color of the tabbar that lists open files)
|
||||
* indent-char (Color of the character which indicates tabs if the option is
|
||||
enabled)
|
||||
* statusline ( Color of the statusline)
|
||||
* tabbar ( Color of the tabbar that lists open files.)
|
||||
* indent-char ( Color of the character which indicates tabs if the option is enabled)
|
||||
* line-number
|
||||
* gutter-error
|
||||
* gutter-warning
|
||||
@@ -143,30 +150,29 @@ Here is a list of the colorscheme groups that you can use:
|
||||
* current-line-number
|
||||
* color-column
|
||||
* ignore
|
||||
* divider (Color of the divider between vertical splits)
|
||||
* divider ( Color of the divider between vertical splits. )
|
||||
|
||||
Colorschemes must be placed in the `~/.config/micro/colorschemes` directory to
|
||||
be used.
|
||||
Colorschemes must be placed in the `~/.config/micro/colorschemes` directory to be used.
|
||||
|
||||
---
|
||||
|
||||
In addition to the main colorscheme groups, there are subgroups that you can
|
||||
specify by adding `.subgroup` to the group. If you're creating your own custom
|
||||
syntax files, you can make use of your own subgroups.
|
||||
specify by adding `.subgroup` to the group. If you're creating your own
|
||||
custom syntax files, you can make use of your own subgroups.
|
||||
|
||||
If micro can't match the subgroup, it'll default to the root group, so it's
|
||||
safe and recommended to use subgroups in your custom syntax files.
|
||||
If micro can't match the subgroup, it'll default to the root group, so
|
||||
it's safe and recommended to use subgroups in your custom syntax files.
|
||||
|
||||
For example if `constant.string` is found in your colorscheme, micro will us
|
||||
that for highlighting strings. If it's not found, it will use constant instead.
|
||||
Micro tries to match the largest set of groups it can find in the colorscheme
|
||||
definitions, so if, for examle `constant.bool.true` is found then micro will use
|
||||
that. If `constant.bool.true` is not found but `constant.bool` is found micro
|
||||
will use `constant.bool`. If not, it uses `constant`.
|
||||
For example if `constant.string` is found in your colorscheme, micro will
|
||||
use that for highlighting strings. If it's not found, it will use constant
|
||||
instead. Micro tries to match the largest set of groups it can find in the
|
||||
colorscheme definitions, so if, for examle `constant.bool.true` is found then
|
||||
micro will use that. If `constant.bool.true` is not found but `constant.bool`
|
||||
is found micro will use `constant.bool`. If not, it uses `constant`.
|
||||
|
||||
Here's a list of subgroups used in micro's built-in syntax files.
|
||||
|
||||
* comment.bright (Some filetypes have distinctions between types of comments)
|
||||
* comment.bright ( Some filetypes have distinctions between types of comments.)
|
||||
* constant.bool
|
||||
* constant.bool.true
|
||||
* constant.bool.false
|
||||
@@ -174,32 +180,29 @@ Here's a list of subgroups used in micro's built-in syntax files.
|
||||
* constant.specialChar
|
||||
* constant.string
|
||||
* constant.string.url
|
||||
* identifier.class (Also used for functions)
|
||||
* identifier.class ( Also used for functions. )
|
||||
* identifier.macro
|
||||
* identifier.var
|
||||
* preproc.shebang (The #! at the beginning of a file that tells the os what
|
||||
script interpreter to use)
|
||||
* symbol.brackets (`{}()[]` and sometimes `<>`)
|
||||
* symbol.operator (Color operator symbols differently)
|
||||
* symbol.tag (For html tags, among other things)
|
||||
* type.keyword (If you want a special highlight for keywords like `private`)
|
||||
* preproc.shebang ( The #! at the beginning of a file that tells the os what script interpreter to use. )
|
||||
* symbol.brackets ( {}()[] and sometimes <> )
|
||||
* symbol.operator ( Color operator symbols differently. )
|
||||
* symbol.tag ( For html tags, among other things.)
|
||||
* type.keyword ( If you want a special highlight for keywords like `private` )
|
||||
|
||||
In the future, plugins may also be able to use color groups for styling.
|
||||
|
||||
|
||||
## Syntax files
|
||||
|
||||
The syntax files is written in yaml-format and specify how to highlight
|
||||
languages.
|
||||
The syntax files is written in yaml-format and specify how to highlight languages.
|
||||
|
||||
Micro's builtin syntax highlighting tries very hard to be sane, sensible and
|
||||
provide ample coverage of the meaningful elements of a language. Micro has
|
||||
syntax files built in for over 100 languages now! However, there may be
|
||||
situations where you find Micro's highlighting to be insufficient or not to your
|
||||
liking. The good news is that you can create your own syntax files, and place them
|
||||
in `~/.config/micro/syntax` and Micro will use those instead.
|
||||
Micro's builtin syntax highlighting tries very hard to be sane, sensible
|
||||
and provide ample coverage of the meaningful elements of a language. Micro has
|
||||
syntax files built int for over 100 languages now. However, there may be
|
||||
situations where you find Micro's highlighting to be insufficient or not to
|
||||
your liking. Good news is you can create syntax files (.micro extension), place them in
|
||||
`~/.config/micro/syntax` and Micro will use those instead.
|
||||
|
||||
### Filetype definition
|
||||
### Filetype defintion
|
||||
|
||||
You must start the syntax file by declaring the filetype:
|
||||
|
||||
@@ -216,9 +219,8 @@ detect:
|
||||
filename: "\\.go$"
|
||||
```
|
||||
|
||||
Micro will match this regex against a given filename to detect the filetype. You
|
||||
may also provide an optional `header` regex that will check the first line of
|
||||
the file. For example:
|
||||
Micro will match this regex against a given filename to detect the filetype. You may also
|
||||
provide an optional `header` regex that will check the first line of the file. For example:
|
||||
|
||||
```
|
||||
detect:
|
||||
@@ -228,10 +230,9 @@ detect:
|
||||
|
||||
#### Syntax rules
|
||||
|
||||
Next you must provide the syntax highlighting rules. There are two types of
|
||||
rules: patterns and regions. A pattern is matched on a single line and usually a
|
||||
single word as well. A region highlights between two patterns over multiple
|
||||
lines and may have rules of its own inside the region.
|
||||
Next you must provide the syntax highlighting rules. There are two types of rules: patterns and regions.
|
||||
A pattern is matched on a single line and usually a single word as well. A region highlights between two
|
||||
patterns over multiple lines and may have rules of its own inside the region.
|
||||
|
||||
Here are some example patterns in Go:
|
||||
|
||||
@@ -242,8 +243,7 @@ rules:
|
||||
- preproc: "\\b(package|import|const|var|type|struct|func|go|defer|iota)\\b"
|
||||
```
|
||||
|
||||
The order of patterns does matter as patterns lower in the file will overwrite
|
||||
the ones defined above them.
|
||||
The order of patterns does matter as patterns lower in the file will overwrite the ones defined above them.
|
||||
|
||||
And here are some example regions for Go:
|
||||
|
||||
@@ -269,15 +269,12 @@ And here are some example regions for Go:
|
||||
- todo: "(TODO|XXX|FIXME):?"
|
||||
```
|
||||
|
||||
Notice how the regions may contain rules inside of them. Any inner rules that
|
||||
are matched are then skipped when searching for the end of the region. For
|
||||
example, when highlighting `"foo \" bar"`, since `\"` is matched by an inner
|
||||
rule in the region, it is skipped. Likewise for `"foo \\" bar`, since `\\` is
|
||||
matched by an inner rule, it is skipped, and then the `"` is found and the
|
||||
string ends at the correct place.
|
||||
Notice how the regions may contain rules inside of them. Any inner rules that are matched are then skipped when searching
|
||||
for the end of the region. For example, when highlighting `"foo \" bar"`, since `\"` is matched by an inner rule in the
|
||||
region, it is skipped. Likewise for `"foo \\" bar`, since `\\` is matched by an inner rule, it is skipped, and then the `"`
|
||||
is found and the string ends at the correct place.
|
||||
|
||||
You may also explicitly mark skip regexes if you don't want them to be
|
||||
highlighted. For example:
|
||||
You may also explicitly mark skip regexes if you don't want them to be highlighted. For example:
|
||||
|
||||
```
|
||||
- constant.string:
|
||||
@@ -289,8 +286,8 @@ highlighted. For example:
|
||||
|
||||
#### Includes
|
||||
|
||||
You may also include rules from other syntax files as embedded languages. For
|
||||
example, the following is possible for html:
|
||||
You may also include rules from other syntax files as embedded languages. For example, the following is possible
|
||||
for html:
|
||||
|
||||
```
|
||||
- default:
|
||||
|
||||
@@ -5,24 +5,19 @@ Here are the possible commands that you can use.
|
||||
|
||||
* `quit`: Quits micro.
|
||||
|
||||
* `save filename?`: Saves the current buffer. If the filename is provided it
|
||||
will 'save as' the filename.
|
||||
* `save filename?`: Saves the current buffer. If the filename is provided it will
|
||||
'save as' the filename.
|
||||
|
||||
* `replace "search" "value" flags`: This will replace `search` with `value`.
|
||||
The `flags` are optional. Possible flags are:
|
||||
* `-a`: Replace all occurrences at once
|
||||
* `-l`: Do a literal search instead of a regex search
|
||||
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.
|
||||
|
||||
Note that `search` must be a valid regex (unless `-l` is passed). If one
|
||||
of the arguments does not have any spaces in it, you may omit the quotes.
|
||||
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.
|
||||
* `set option value`: sets the option to value. See the `options` help topic
|
||||
for a list of options you can set.
|
||||
|
||||
* `setlocal option value`: sets the option to value locally (only in the current
|
||||
buffer).
|
||||
@@ -30,25 +25,27 @@ Here are the possible commands that you can use.
|
||||
* `show option`: shows the current value of the given option.
|
||||
|
||||
* `eval "expression"`: Evaluates a Lua expression. Note that micro will not
|
||||
print anything so you should use `messenger:Message(...)` to display a value.
|
||||
print anything so you should use `messenger:Message(...)` to display a
|
||||
value.
|
||||
|
||||
* `run sh-command`: runs the given shell command in the background. The
|
||||
command's output will be displayed in one line when it finishes running.
|
||||
|
||||
* `bind key action`: creates a keybinding from key to action. See the sections
|
||||
on keybindings above for more info about what keys and actions are available.
|
||||
* `bind key action`: creates a keybinding from key to action. See the sections on
|
||||
keybindings above for more info about what keys and actions are available.
|
||||
|
||||
* `vsplit filename`: opens a vertical split with `filename`. If no filename is
|
||||
provided, a vertical split is opened with an empty buffer.
|
||||
|
||||
* `hsplit filename`: same as `vsplit` but opens a horizontal split instead of a
|
||||
vertical split.
|
||||
* `hsplit filename`: same as `vsplit` but opens a horizontal split instead of
|
||||
a vertical split.
|
||||
|
||||
* `tab filename`: opens the given file in a new tab.
|
||||
|
||||
* `tabswitch tab`: This command will switch to the specified tab. The `tab` can
|
||||
either be a tab number, or a name of a tab.
|
||||
|
||||
* `tabswitch tab`: This command will switch to the specified tab.
|
||||
The `tab` can either be a tab number, or a name of a tab.
|
||||
|
||||
|
||||
* `log`: opens a log of all messages and debug statements.
|
||||
|
||||
* `plugin install plugin_name`: installs the given plugin.
|
||||
@@ -59,15 +56,15 @@ Here are the possible commands that you can use.
|
||||
|
||||
* `plugin update`: updates all installed plugins.
|
||||
|
||||
* `plugin search plugin_name`: searches for the given plugin. Note that you can
|
||||
find a list of all available plugins at
|
||||
* `plugin search plugin_name`: searches for the given plugin.
|
||||
Note that you can find a list of all available plugins at
|
||||
github.com/micro-editor/plugin-channel.
|
||||
|
||||
You can also see more information about the plugin manager in the
|
||||
`Plugin Manager` section of the `plugins` help topic.
|
||||
You can also see more information about the plugin manager
|
||||
in the `Plugin Manager` section of the `plugins` help topic.
|
||||
|
||||
* `plugin available`: list plugins available for download (this includes any
|
||||
plugins that may be already installed).
|
||||
* `plugin available`: list plugins available for download (this includes
|
||||
any plugins that may be already installed).
|
||||
|
||||
* `reload`: reloads all runtime files.
|
||||
|
||||
@@ -77,29 +74,8 @@ Here are the possible commands that you can use.
|
||||
|
||||
* `open filename`: Open a file in the current buffer.
|
||||
|
||||
* `retab`: Replaces all leading tabs with spaces or leading spaces with tabs
|
||||
depending on the value of `tabstospaces`.
|
||||
|
||||
* `raw`: Micro will open a new tab and show the escape sequence for every event
|
||||
it receives from the terminal. This shows you what micro actually sees from
|
||||
the terminal and helps you see which bindings aren't possible and why. This
|
||||
is most useful for debugging keybindings.
|
||||
|
||||
* `showkey`: Show the action(s) bound to a given key. For example
|
||||
running `> showkey CtrlC` will display `main.(*View).Copy`. Unfortuately
|
||||
showkey does not work well for keys bound to plugin actions. For those
|
||||
it just shows "LuaFunctionBinding."
|
||||
|
||||
---
|
||||
|
||||
The following commands are provided by the default plugins:
|
||||
|
||||
* `lint`: Lint the current file for errors.
|
||||
|
||||
# Command Parsing
|
||||
|
||||
When running a command, you can use extra syntax that micro will expand before
|
||||
running the command. To use an argument with a space in it, simply put it in
|
||||
quotes. You can also use environment variables in the command bar and they
|
||||
will be expanded to their value. Finally, you can put an expression in backticks
|
||||
and it will be evaluated by the shell beforehand.
|
||||
|
||||
@@ -1,128 +1,224 @@
|
||||
# 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`
|
||||
Below are simple charts of the default hotkeys and their functions.
|
||||
For more information about binding custom hotkeys or changing
|
||||
default bindings, please run `>help keybindings`
|
||||
|
||||
Please remember that *all* keys here are rebindable! If you don't like it, you
|
||||
can change it!
|
||||
Please remember that *all* keys here are rebindable!
|
||||
If you don't like it, you can change it!
|
||||
|
||||
### Power user
|
||||
(We are not responsible for you forgetting what you bind keys to.
|
||||
Do not open an issue because you forgot your keybindings.)
|
||||
|
||||
| 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). |
|
||||
#Power user
|
||||
+--------+---------------------------------------------------------+
|
||||
| Key | Description of function |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Ctrl+E | Switch to the micro command prompt to run a command. |
|
||||
| | (See `>help commands` for a list of commands. ) |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Tab | In command prompt it will auto complete if possible. |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Ctrl+B | Run shell commands in micro's current working directory.|
|
||||
+--------+---------------------------------------------------------+
|
||||
|
||||
### Navigation
|
||||
#Navigation
|
||||
|--------+---------------------------------------------------------+
|
||||
| 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 | |
|
||||
| or | |
|
||||
| Ctrl+ | Move to the beginning of the current line. (Naturally.) |
|
||||
| Left | |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| End | |
|
||||
| or | |
|
||||
| Ctrl+ | Move to the end of the current line. |
|
||||
| Right | |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Alt+ | |
|
||||
| Left | Move cursor one complete word left. |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Alt+ | |
|
||||
| Right | Move cursor one complete word right. |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| PageUp | Move cursor up lines quickly. |
|
||||
+--------+---------------------------------------------------------+
|
||||
| PageDn | Move cursor down lines quickly. |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Ctrl+ | |
|
||||
| Home | |
|
||||
| or | Move cursor to start of the document |
|
||||
| Ctrl+ | |
|
||||
| Up | |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Ctrl+ | |
|
||||
| End | |
|
||||
| or | Move cursor to end of the document |
|
||||
| Ctrl+ | |
|
||||
| Down | |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| 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`) |
|
||||
+--------+---------------------------------------------------------+
|
||||
|
||||
| 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 |
|
||||
| Alt+{ | Move cursor to previous empty line, or beginning of document |
|
||||
| Alt+} | Move cursor to next empty line, or end of document |
|
||||
| PageUp | Move cursor up one page |
|
||||
| PageDown | Move cursor down one page |
|
||||
| CtrlHome or CtrlUpArrow | Move cursor to start of document |
|
||||
| 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) |
|
||||
#Tabs
|
||||
+--------+---------------------------------------------------------+
|
||||
| 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. |
|
||||
+--------+---------------------------------------------------------+
|
||||
|
||||
### Tabs
|
||||
#Find Operations
|
||||
+--------+---------------------------------------------------------+
|
||||
| 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
|
||||
+--------+---------------------------------------------------------+
|
||||
| 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. |
|
||||
+--------+---------------------------------------------------------+
|
||||
|
||||
### Find Operations
|
||||
#Text operations
|
||||
+--------+---------------------------------------------------------+
|
||||
| Ctrl+A | Select all text in current file. |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Alt+ | |
|
||||
| Shift+ | Select complete word right. |
|
||||
| Right | |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Alt+ | |
|
||||
| Shift+ | Select complete word left. |
|
||||
| Left | |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Shift+ | |
|
||||
| Home | |
|
||||
| or | Select from the current cursor position to the |
|
||||
| Ctrl+ | start of the current line. |
|
||||
| Shift+ | |
|
||||
| Left | |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Shift+ | |
|
||||
| End | |
|
||||
| or | Select from the current cursor position to the |
|
||||
| Ctrl+ | end of the current line. |
|
||||
| Shift+ | |
|
||||
| Right | |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Ctrl+ | |
|
||||
| Shift+ | Select from the current cursor position to the |
|
||||
| Up | start of the document. |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Ctrl+ | |
|
||||
| Shift+ | Select from the current cursor position to the |
|
||||
| Down | end of the document. |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| 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. |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Alt+ | |
|
||||
| Up | Move current line or selected lines up. |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Alt+ | |
|
||||
| Down | Move current line or selected lines down. |
|
||||
| Arrow | |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Alt+ | |
|
||||
| Ctrl+H | |
|
||||
| or | Delete complete word left. |
|
||||
| Alt+ | |
|
||||
| Back- | |
|
||||
| space | |
|
||||
+--------+---------------------------------------------------------+
|
||||
|
||||
| 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 |
|
||||
#Macros
|
||||
+--------+---------------------------------------------------------+
|
||||
| | Toggle ON/OFF macro recording. |
|
||||
| Ctrl+U | Simply press Ctrl+U to begin recording a macro and |
|
||||
| | press Ctrl+U to stop recording macro. |
|
||||
+--------+---------------------------------------------------------+
|
||||
| Ctrl+J | Run your recorded macro. |
|
||||
+--------+---------------------------------------------------------+
|
||||
|
||||
### File Operations
|
||||
#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.) |
|
||||
+--------+---------------------------------------------------------+
|
||||
|
||||
| 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 |
|
||||
|
||||
### Text operations
|
||||
|
||||
| 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 |
|
||||
|
||||
### Macros
|
||||
|
||||
| Key | Description of function |
|
||||
|-------- |---------------------------------------------------------------------------------- |
|
||||
| Ctrl+U | Toggle macro recording (press Ctrl+U to start recording and press again to stop) |
|
||||
| Ctrl+J | Run latest recorded macro |
|
||||
|
||||
### Multiple cursors
|
||||
|
||||
| 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.
|
||||
#Emacs style actions
|
||||
+--------+---------------------------------------------------------+
|
||||
| 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. ) |
|
||||
+--------+---------------------------------------------------------+
|
||||
|
||||
#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. |
|
||||
+--------+---------------------------------------------------------+
|
||||
|
||||
| Key | Description of function |
|
||||
|----- |------------------------- |
|
||||
| F1 | Open help |
|
||||
| F2 | Save |
|
||||
| F3 | Find |
|
||||
| F4 | Quit |
|
||||
| F7 | Find |
|
||||
| F10 | Quit |
|
||||
|
||||
@@ -8,7 +8,7 @@ We have included a few colorschemes that are for fun:
|
||||
Nintendo Entertainment System color palette.
|
||||
* symbian-tc: Colorscheme based on SymbOS's GUI.
|
||||
* matrix: Pretend it's 1981 with a colorscheme based on a monochrome
|
||||
IBM 5151. (Does not include the ghosting and trailing)
|
||||
IBM 5151. ( Does not include the ghosting and trailing. )
|
||||
|
||||
Check the plugin repo periodically for gimmick-color extension packs and genuine
|
||||
additional themes.
|
||||
Check the plugin repo periodically for gimmick-color extension packs
|
||||
and genuine additional themes.
|
||||
@@ -1,62 +1,51 @@
|
||||
# Micro help text
|
||||
|
||||
Thank you for downloading and using micro.
|
||||
|
||||
Micro is a terminal-based text editor that aims to be easy to use and intuitive,
|
||||
while also taking advantage of the full capabilities of modern terminals.
|
||||
|
||||
|
||||
|
||||
For a list of the default keybindings press CtrlE and type `help defaultkeys`.
|
||||
For more information on keybindings see `> help keybindings`.
|
||||
If you want to see all the keybindings press CtrlE and type `help keybindings`.
|
||||
|
||||
See the next section for more information about documentation and help.
|
||||
|
||||
## Quick-start
|
||||
### Quick-start
|
||||
|
||||
Press CtrlQ to quit, and CtrlS to save. Press CtrlE to start typing commands and
|
||||
you can see which commands are available by pressing tab, or by viewing the help
|
||||
topic `> help commands`. When I write `> ...` I mean press CtrlE and then type
|
||||
whatever is there.
|
||||
Press CtrlQ to quit, and CtrlS to save. Press CtrlE to start typing commands
|
||||
and you can see which commands are available by pressing tab, or by
|
||||
viewing the help topic `> help commands`. When I write `> ...` I mean press
|
||||
CtrlE and then type whatever is there.
|
||||
|
||||
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`.
|
||||
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`
|
||||
|
||||
If the colorscheme doesn't look good, you can change it with
|
||||
`> set colorscheme ...`. You can press tab to see the available colorschemes, or
|
||||
see more information with `> help colors`.
|
||||
If the colorscheme doesn't look good, you can change it with `> set colorscheme ...`.
|
||||
You can press tab to see the available colorschemes, or see more information with
|
||||
`> help colors`.
|
||||
|
||||
Press CtrlW to move between splits, and type `> vsplit filename` or
|
||||
`> hsplit filename` to open a new split.
|
||||
Press CtrlW to move between splits, and type `> vsplit filename` or `> hsplit filename`
|
||||
to open a new split.
|
||||
|
||||
|
||||
## Accessing more help
|
||||
### Accessing more help
|
||||
|
||||
Micro has a built-in help system much like Vim's (although less extensive).
|
||||
|
||||
To use it, press CtrlE to access command mode and type in `help` followed by a
|
||||
topic. Typing `help` followed by nothing will open this page.
|
||||
To use it, press CtrlE to access command mode and type in `help` followed by a topic.
|
||||
Typing `help` followed by nothing will open this page.
|
||||
|
||||
Here are the possible help topics that you can read:
|
||||
|
||||
* tutorial: A brief tutorial which gives an overview of all the other help
|
||||
topics
|
||||
* keybindings: Gives a full list of the default keybindings as well as how to
|
||||
rebind them
|
||||
* defaultkeys: Gives a more straight-forward list of the hotkey commands and what
|
||||
they do.
|
||||
* tutorial: A brief tutorial which gives an overview of all the other help topics
|
||||
* keybindings: Gives a full list of the default keybindings as well as how to rebind them
|
||||
* defaultkeys: Gives a more straight-forward list of the hotkey commands and what they do.
|
||||
* commands: Gives a list of all the commands and what they do
|
||||
* options: Gives a list of all the options you can customize
|
||||
* plugins: Explains how micro's plugin system works and how to create your own
|
||||
plugins
|
||||
* colors: Explains micro's colorscheme and syntax highlighting engine and how to
|
||||
create your own colorschemes or add new languages to the engine
|
||||
* plugins: Explains how micro's plugin system works and how to create your own plugins
|
||||
* colors: Explains micro's colorscheme and syntax highlighting engine and how to create your
|
||||
own colorschemes or add new languages to the engine
|
||||
|
||||
For example, to open the help page on plugins you would press CtrlE and type
|
||||
`help plugins`.
|
||||
For example, to open the help page on plugins you would press CtrlE and type `help plugins`.
|
||||
|
||||
I recommend looking at the `tutorial` help file because it is short for each
|
||||
section and gives concrete examples of how to use the various configuration
|
||||
options in micro. However, it does not give the in-depth documentation that the
|
||||
other topics provide.
|
||||
I recommend looking at the `tutorial` help file because it is short for each section and
|
||||
gives concrete examples of how to use the various configuration options in micro. However,
|
||||
it does not give the in-depth documentation that the other topics provide.
|
||||
|
||||
@@ -2,28 +2,25 @@
|
||||
|
||||
Micro has a plethora of hotkeys that make it easy and powerful to use and all
|
||||
hotkeys are fully customizable to your liking.
|
||||
|
||||
Custom keybindings are stored internally in micro if changed with the `> bind`
|
||||
command or you can also be added in the file `~/.config/micro/bindings.json` as
|
||||
discussed below. For a list of the default keybindings in the json format used
|
||||
by micro, please see the end of this file. For a more user-friendly list with
|
||||
explanations of what the default hotkeys are and what they do, please see
|
||||
`>help defaultkeys`
|
||||
Custom keybindings are stored internally in micro if changed with the `>bind` command or
|
||||
you can also be added in the file `~/.config/micro/bindings.json` as discussed below.
|
||||
For a list of the default keybindings in the json format used by micro, please see
|
||||
the end of this file. For a more user-friendly list with explanations of what the default
|
||||
hotkeys are and what they do, please see `>help defaultkeys`
|
||||
|
||||
If `~/.config/micro/bindings.json` does not exist, you can simply create it.
|
||||
Micro will know what to do with it.
|
||||
|
||||
You can use the alt keys + arrows to move word by word. Ctrl left and right move
|
||||
the cursor to the start and end of the line, and ctrl up and down move the
|
||||
cursor the start and end of the buffer.
|
||||
You can use the alt keys + arrows to move word by word.
|
||||
Ctrl left and right move the cursor to the start and end of the line, and
|
||||
ctrl up and down move the cursor the start and end of the buffer.
|
||||
|
||||
You can hold shift with all of these movement actions to select while moving.
|
||||
|
||||
# Rebinding keys
|
||||
|
||||
## Rebinding keys
|
||||
|
||||
The bindings may be rebound using the `~/.config/micro/bindings.json` file. Each
|
||||
key is bound to an action.
|
||||
The bindings may be rebound using the `~/.config/micro/bindings.json`
|
||||
file. Each key is bound to an action.
|
||||
|
||||
For example, to bind `Ctrl-y` to undo and `Ctrl-z` to redo, you could put the
|
||||
following in the `bindings.json` file.
|
||||
@@ -38,8 +35,8 @@ following in the `bindings.json` file.
|
||||
In addition to editing your `~/.config/micro/bindings.json`, you can run
|
||||
`>bind <keycombo> <action>` For a list of bindable actions, see below.
|
||||
|
||||
You can also chain commands when rebinding. For example, if you want Alt-s to
|
||||
save and quit you can bind it like so:
|
||||
You can also chain commands when rebinding. For example, if you want Alt-s to save
|
||||
and quit you can bind it like so:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -47,86 +44,12 @@ save and quit you can bind it like so:
|
||||
}
|
||||
```
|
||||
|
||||
## Binding commands
|
||||
|
||||
You can also bind a key to execute a command in command mode (see
|
||||
`help commands`). Simply prepend the binding with `command:`. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"Alt-p": "command:pwd"
|
||||
}
|
||||
```
|
||||
|
||||
Now when you press `Alt-p` the `pwd` command will be executed which will show
|
||||
your working directory in the infobar.
|
||||
|
||||
You can also bind an "editable" command with `command-edit:`. This means that
|
||||
micro won't immediately execute the command when you press the binding, but
|
||||
instead just place the string in the infobar in command mode. For example,
|
||||
you could rebind `CtrlG` to `> help`:
|
||||
|
||||
```json
|
||||
{
|
||||
"CtrlG": "command-edit:help "
|
||||
}
|
||||
```
|
||||
|
||||
Now when you press `CtrlG`, `help` will appear in the command bar and your cursor will
|
||||
be placed after it (note the space in the json that controls the cursor placement).
|
||||
|
||||
## Binding raw escape sequences
|
||||
|
||||
Only read this section if you are interested in binding keys that aren't on the
|
||||
list of supported keys for binding.
|
||||
|
||||
One of the drawbacks of using a terminal-based editor is that the editor must
|
||||
get all of its information about key events through the terminal. The terminal
|
||||
sends these events in the form of escape sequences often (but not always)
|
||||
starting with `0x1b`.
|
||||
|
||||
For example, if micro reads `\x1b[1;5D`, on most terminals this will mean the
|
||||
user pressed CtrlLeft.
|
||||
|
||||
For many key chords though, the terminal won't send any escape code or will send
|
||||
an escape code already in use. For example for `CtrlBackspace`, my terminal
|
||||
sends `\u007f` (note this doesn't start with `0x1b`), which it also sends for
|
||||
`Backspace` meaning micro can't bind `CtrlBackspace`.
|
||||
|
||||
However, some terminals do allow you to bind keys to send specific escape
|
||||
sequences you define. Then from micro you can directly bind those escape
|
||||
sequences to actions. For example, to bind `CtrlBackspace` you can instruct your
|
||||
terminal to send `\x1bctrlback` and then bind it in `bindings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"\u001bctrlback": "DeleteWordLeft"
|
||||
}
|
||||
```
|
||||
|
||||
Here are some instructions for sending raw escapes in different terminals
|
||||
|
||||
### iTerm2
|
||||
|
||||
In iTerm2, you can do this in `Preferences->Profiles->Keys` then click the `+`,
|
||||
input your keybinding, and for the `Action` select `Send Escape Sequence`. For
|
||||
the above example your would type `ctrlback` into the box (the `\x1b`) is
|
||||
automatically sent by iTerm2.
|
||||
|
||||
### Linux using loadkeys
|
||||
|
||||
You can do this in linux using the loadkeys program.
|
||||
|
||||
Coming soon!
|
||||
|
||||
|
||||
## Unbinding keys
|
||||
# Unbinding keys
|
||||
|
||||
It is also possible to disable any of the default key bindings by use of the
|
||||
`UnbindKey` action in the user's `bindings.json` file.
|
||||
|
||||
|
||||
## Bindable actions and bindable keys
|
||||
# Bindable actions and bindable keys
|
||||
|
||||
The list of default keybindings contains most of the possible actions and keys
|
||||
which you can use, but not all of them. Here is a full list of both.
|
||||
@@ -156,7 +79,6 @@ MoveLinesUp
|
||||
MoveLinesDown
|
||||
DeleteWordRight
|
||||
DeleteWordLeft
|
||||
SelectLine
|
||||
SelectToStartOfLine
|
||||
SelectToEndOfLine
|
||||
InsertNewline
|
||||
@@ -191,8 +113,6 @@ HalfPageUp
|
||||
HalfPageDown
|
||||
StartOfLine
|
||||
EndOfLine
|
||||
ParagraphPrevious
|
||||
ParagraphNext
|
||||
ToggleHelp
|
||||
ToggleRuler
|
||||
JumpLine
|
||||
@@ -211,22 +131,7 @@ HSplit
|
||||
PreviousSplit
|
||||
ToggleMacro
|
||||
PlayMacro
|
||||
Suspend (Unix only)
|
||||
ScrollUp
|
||||
ScrollDown
|
||||
SpawnMultiCursor
|
||||
RemoveMultiCursor
|
||||
RemoveAllMultiCursors
|
||||
SkipMultiCursor
|
||||
UnbindKey
|
||||
JumpToMatchingBrace
|
||||
```
|
||||
|
||||
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:
|
||||
@@ -357,19 +262,6 @@ 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
|
||||
@@ -384,10 +276,10 @@ MouseWheelRight
|
||||
"ShiftRight": "SelectRight",
|
||||
"AltLeft": "WordLeft",
|
||||
"AltRight": "WordRight",
|
||||
"AltUp": "MoveLinesUp",
|
||||
"AltDown": "MoveLinesDown",
|
||||
"AltShiftRight": "SelectWordRight",
|
||||
"AltShiftLeft": "SelectWordLeft",
|
||||
"AltUp": "MoveLinesUp",
|
||||
"AltDown": "MoveLinesDown",
|
||||
"CtrlLeft": "StartOfLine",
|
||||
"CtrlRight": "EndOfLine",
|
||||
"CtrlShiftLeft": "SelectToStartOfLine",
|
||||
@@ -398,15 +290,14 @@ MouseWheelRight
|
||||
"CtrlDown": "CursorEnd",
|
||||
"CtrlShiftUp": "SelectToStart",
|
||||
"CtrlShiftDown": "SelectToEnd",
|
||||
"Alt-{": "ParagraphPrevious",
|
||||
"Alt-}": "ParagraphNext",
|
||||
"Enter": "InsertNewline",
|
||||
"Space": "InsertSpace",
|
||||
"CtrlH": "Backspace",
|
||||
"Backspace": "Backspace",
|
||||
"Alt-CtrlH": "DeleteWordLeft",
|
||||
"Alt-Backspace": "DeleteWordLeft",
|
||||
"Tab": "IndentSelection,InsertTab",
|
||||
"Backtab": "OutdentSelection,OutdentLine",
|
||||
"Backtab": "OutdentSelection",
|
||||
"CtrlO": "OpenFile",
|
||||
"CtrlS": "Save",
|
||||
"CtrlF": "Find",
|
||||
@@ -445,6 +336,8 @@ MouseWheelRight
|
||||
"Alt-b": "WordLeft",
|
||||
"Alt-a": "StartOfLine",
|
||||
"Alt-e": "EndOfLine",
|
||||
"Alt-p": "CursorUp",
|
||||
"Alt-n": "CursorDown",
|
||||
|
||||
// Integration with file managers
|
||||
"F1": "ToggleHelp",
|
||||
@@ -454,29 +347,15 @@ MouseWheelRight
|
||||
"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.
|
||||
|
||||
Note: On some old terminal emulators and on Windows machines, `CtrlH` should be
|
||||
used for backspace.
|
||||
|
||||
Additionally, alt keys can be bound by using `Alt-key`. For example `Alt-a` or
|
||||
`Alt-Up`. Micro supports an optional `-` between modifiers like `Alt` and
|
||||
`Ctrl` so `Alt-a` could be rewritten as `Alta` (case matters for alt bindings).
|
||||
This is why in the default keybindings you can see `AltShiftLeft` instead of
|
||||
`Alt-ShiftLeft` (they are equivalent).
|
||||
Additionally, alt keys can be bound by using `Alt-key`. For example `Alt-a`
|
||||
or `Alt-Up`. Micro supports an optional `-` between modifiers like `Alt` and `Ctrl`
|
||||
so `Alt-a` could be rewritten as `Alta` (case matters for alt bindings). This is
|
||||
why in the default keybindings you can see `AltShiftLeft` instead of `Alt-ShiftLeft`
|
||||
(they are equivalent).
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user