Compare commits

...

92 Commits

Author SHA1 Message Date
Zachary Yedidia
af520cf047 Fix terminal emulator support 2018-01-25 20:10:49 -05:00
Zachary Yedidia
db75e11e32 Update tcell 2018-01-24 16:11:48 -05:00
Zachary Yedidia
797e5cc27f Update tcell 2018-01-22 23:40:42 -05:00
Zachary Yedidia
36dc6647dd Add new shell command documentation
Ref #979
2018-01-22 21:03:52 -05:00
Zachary Yedidia
44b64f7129 Fix compile error 2018-01-22 17:32:30 -05:00
Zachary Yedidia
0a49ea0a0d Improve shell commands 2018-01-22 17:20:03 -05:00
Zachary Yedidia
4f41881c10 Make onViewOpen and onBufferOpen the same
Ref #948
2018-01-22 15:27:56 -05:00
Zachary Yedidia
63299df4b9 Don't throw error if job callback doesn't exist
Closes #953
2018-01-21 16:31:13 -05:00
Zachary Yedidia
10b8fb7b26 Expose emulator functions and support output
Ref #979
2018-01-20 23:34:16 -05:00
Zachary Yedidia
0a7e4c8f06 Use zyedidia/pty instead of kr/pty 2018-01-20 22:28:17 -05:00
Zachary Yedidia
83190a578e Change HandleShellCommand backend
I'm trying to add more options for plugins that want to run shell
commands. Also trying to add support for running shell commands in the
terminal emulator from a plugin and return the output.

More to come soon.

Ref #979
2018-01-20 22:23:52 -05:00
Zachary Yedidia
79349562b2 Improve unicode softwrap drawing
Ref #1002
Ref #909
2018-01-20 12:36:22 -05:00
Zachary Yedidia
0cb1ad09cd Merge 2018-01-19 00:28:58 -05:00
Zachary Yedidia
6ef00c4c3b Clean up terminal emulator a bit 2018-01-19 00:28:51 -05:00
Zachary Yedidia
bb598ae566 Merge pull request #999 from sum01/create_parents
Create parent folders (if none) when saving
2018-01-18 00:49:45 -05:00
Zachary Yedidia
13c63a9951 Merge pull request #1001 from sum01/makefile_syntax
Fix Makefile equals highlighting
2018-01-17 23:59:36 -05:00
sum01
cf06d06fb3 Fix Makefile = highlighting
I think they weren't being highlighted at all, leading to a weird looking default white box around them.
2018-01-17 23:44:53 -05:00
sum01
808e3a7c9f Prompt to create parent folders (if none) when saving
Fixes #995
2018-01-17 20:59:19 -05:00
Zachary Yedidia
16e9068cb9 Support line:col in JumpLine
Closes #1000
2018-01-17 19:09:50 -05:00
Zachary Yedidia
3924e363d1 Fix minor autoindent issue
Fixes #985
2018-01-17 17:37:17 -05:00
Zachary Yedidia
a274daeaaf Merge pull request #998 from JoshuaRLi/select-line-action
Implemented SelectLine as an Action
2018-01-17 17:25:37 -05:00
Zachary Yedidia
e26417fd14 Fix shebang js highlighting and js division
Closes #901
Closes #994
2018-01-17 17:19:03 -05:00
Joshua Li
d7ba2f600e implemented select line as an Action 2018-01-16 17:27:15 -05:00
Zachary Yedidia
1cf4baa743 Don't use indentchar style if disabled
Fixes #990
2018-01-14 11:23:30 -08:00
Zachary Yedidia
7e3aa337f6 Fix autocomplete on empty prompt 2018-01-10 15:41:49 -05:00
Zachary Yedidia
3f01101da4 Add onBufferOpen plugin callback
Closes #948
2018-01-08 17:08:11 -05:00
Zachary Yedidia
9a6054fc43 Add GetMouseClickLocation to view 2018-01-08 16:54:27 -05:00
Zachary Yedidia
b2a0745747 Update third-party licenses 2018-01-08 16:41:26 -05:00
Zachary Yedidia
7911ce1f16 Remove duplicate utf8 code 2018-01-08 16:38:59 -05:00
Zachary Yedidia
8bff7f00d0 Change docs to use true/false instead of on/off
Closes #976
2018-01-08 15:21:32 -05:00
Zachary Yedidia
957273fc92 Add railscast colorscheme
From https://github.com/pbsds/micro-railscast-theme
2018-01-07 21:02:24 -05:00
Zachary Yedidia
805d6ccaf7 Don't brace highlight with selection 2018-01-07 20:58:01 -05:00
Zachary Yedidia
fc2566a0de Add JumpToMatchingBrace action
This commit adds the JumpToMatchingBrace action which lets the cursor
jump to a matching brace if it is on one.

Closes #853
2018-01-07 16:17:22 -05:00
Zachary Yedidia
86c08bd747 Add brace highlighting
Use the 'matchbrace' option which is off by default.

Ref #853
2018-01-07 15:50:08 -05:00
Zachary Yedidia
0b47502e62 Fix minor issue with indent/outdent selection
Fixes #984
2018-01-06 16:04:18 -05:00
Zachary Yedidia
2afbcef825 Update readme 2018-01-05 23:05:20 -05:00
Zachary Yedidia
0a500be3ba Merge pull request #877 from IOAyman/readme
Added ToC in README
2018-01-05 23:04:52 -05:00
Zachary Yedidia
3b36316b00 Add support for selection and copy in terminal
This commit adds mouse and copy support in the terminal emulator
in micro.
2018-01-05 22:44:36 -05:00
Zachary Yedidia
d668050ebe Merge 2018-01-05 21:39:03 -05:00
Zachary Yedidia
dd47f167f1 Clean up terminal a bit and wait before closing 2018-01-05 21:38:40 -05:00
Zachary Yedidia
2ebeb9d5a5 Merge pull request #982 from sum01/syntax-touchups
Remove weird ignore on git-commit
2018-01-05 14:06:38 -05:00
sum01
8629357c70 Remove weird ignore on git-commit
It was needlessly highlighting everything that wasn't a comment.

Adds keyword detection for Github-esque issue-closing syntax.
Adds missing 'd' and 'drop' highlighting in git-rebase-todo
2018-01-05 03:41:50 -05:00
Zachary Yedidia
c8ff764467 Merge pull request #981 from sum01/fix_import
Fix #980 duplicate import
2018-01-04 22:29:27 -05:00
sum01
8e741599dc Fix #980 duplicate import 2018-01-04 22:27:09 -05:00
Zachary Yedidia
770cb87f7a Fix windows errors 2018-01-04 21:46:44 -05:00
Zachary Yedidia
d82867ee53 Add more comments 2018-01-04 17:14:51 -05:00
Zachary Yedidia
275bce7d69 Add new dependencies 2018-01-04 17:05:49 -05:00
Zachary Yedidia
9094c174cc Initial support for terminal within micro
This commit adds beta support for running a shell or other program
within a micro view.

Use the `> term` command. With no arguments, `term` will open your
shell in interactive mode. You can also run an arbitrary command
with `> term cmd` and the command with be executed and output
shown. One issue at the moment is the terminal window will close
immediately after the process dies.

No mouse events are sent to programs running within micro.

Ref #243
2018-01-04 17:03:08 -05:00
Zachary Yedidia
a814677b51 Improve command bar completion 2018-01-03 21:35:03 -05:00
Zachary Yedidia
8b60e4f3b1 Update colorscheme list in docs
Closes #956
2018-01-02 22:46:24 -05:00
Zachary Yedidia
c32f5a4859 Add basename option
Closes #903
2018-01-02 22:25:55 -05:00
Zachary Yedidia
df44f538fd Improve file save speed for large files 2018-01-02 18:36:29 -05:00
Zachary Yedidia
a4ae7a1e11 More command binding
Now can bind editable commands with `command-edit:`

Ref #974
2018-01-02 15:15:28 -05:00
Zachary Yedidia
70616b335e Merge 2018-01-02 15:03:10 -05:00
Zachary Yedidia
f6e9a16724 Allow binding commands
Bind commands with `command:...`

Ref #974
2018-01-02 15:02:46 -05:00
sum01
ac41e186a0 Add some Lua syntax (#962)
* Add some missing Lua string syntax
All Lua strings have the string functions inside of them.

'...you can use the string functions in object-oriented style'
See '6.4 – String Manipulation' in https://www.lua.org/manual/5.3/manual.html

* Lua - Highlight self and TODO/NOTE/FIXME

* Add Lua 'arg' and triple-dot syntax
2017-12-31 00:37:11 -05:00
Zachary Yedidia
a90cb64265 Merge pull request #971 from mbesancon/patch-1
added const for julia
2017-12-31 00:36:49 -05:00
Zachary Yedidia
5124dd04b3 Merge pull request #973 from sum01/micro_syntax
Fix micro (color) file syntax
2017-12-31 00:36:38 -05:00
sum01
7867d50d67 Fix micro file syntax
Some of the words were missing, so this adds those.
2017-12-30 12:13:52 -05:00
mbesancon
0ba60728e8 added const for julia
const is a base keyword
2017-12-29 13:08:21 -05:00
Zachary Yedidia
981263eb81 Merge 2017-12-28 16:05:53 -05:00
Zachary Yedidia
79deabbbd6 Fix options cmdline message
Ref #969
2017-12-28 16:05:35 -05:00
Zachary Yedidia
ba4b028076 Merge pull request #942 from motet-a/javascript-syntax
Improve JavaScript syntax highlighting
2017-12-28 14:57:49 -05:00
Zachary Yedidia
649e5799c2 Merge pull request #960 from nitsakh/insert-issue
Changes to add support for Insert Key Press
2017-12-28 14:54:31 -05:00
Zachary Yedidia
7339a88d68 Merge pull request #965 from tommyshem/ada
#964 add ada syntax highlighting file
2017-12-28 14:52:09 -05:00
tommy
b0cfb2e691 #964 add ada syntax 2017-12-27 14:27:42 +00:00
Zachary Yedidia
4e0d402cea Merge pull request #961 from sum01/fix_commit_syntax
Fix git-commit comment syntax
2017-12-22 23:06:37 -05:00
sum01
f882248f41 Fix git-commit comment syntax
A comment in a git-commit must have the hash at the start of the line, instead of just anywhere in the line.
2017-12-22 20:02:43 -05:00
Nitish Sakhawalkar
f58c5412a8 Updating to make overwrite mode as an action 2017-12-18 17:11:00 -08:00
Nitish Sakhawalkar
b0e4043513 Changes to add support for Insert Key Press 2017-12-18 13:28:21 -08:00
Antoine Motet
47dd65d4e5 Improve JavaScript syntax highlighting
- Sort keywords alphabetically
- Use `symbol.operator` for operators instead of `statement`
- Add a basic support for back-tick strings
- Mark unassigned keywords as errors
2017-12-17 23:53:58 +01:00
Tommy
fa84f6ddc3 create plugin folders work on windows fix #931 (#951) 2017-12-13 21:53:30 -05:00
Zachary Yedidia
2bf40f096e Don't autosave buffers with no path
Closes #955
2017-12-13 12:43:00 -05:00
Zachary Yedidia
4802403308 Remove android from actions_other build tag
Ref #949
2017-12-10 16:15:16 -05:00
Zachary Yedidia
e443adef31 Merge pull request #946 from tommyshem/luafix
fix lua comment block #929
2017-12-04 15:41:48 -05:00
tommy
cdb057dfc3 fix lua comment block 2017-12-04 20:30:35 +00:00
Zachary Yedidia
9da1ef178e Add support for setting local settings via filetype 2017-12-03 23:38:09 -05:00
Zachary Yedidia
bf33ab532c Store string keys for bindings 2017-12-03 23:15:32 -05:00
Zachary Yedidia
46c7437270 Fix ViewType refactor 2017-12-03 17:19:51 -05:00
Zachary Yedidia
09cab07352 Merge 2017-12-03 16:49:27 -05:00
Zachary Yedidia
b7214da4ea Make ViewType fields public
Ref #904
2017-12-03 16:49:05 -05:00
Bastien Traverse
5138ae2436 Fix typo in tutorial.md (#940)
Delete extraneous "plugins" word in line 12.
2017-12-03 15:16:50 -05:00
Zachary Yedidia
98778a80c2 Allow plugins to create view types
Closes #904
2017-12-03 15:15:07 -05:00
Zachary Yedidia
e0a8e90ad9 Merge 2017-12-03 13:05:50 -05:00
Zachary Yedidia
2ae9f88eaa Add showkey command 2017-12-03 13:05:46 -05:00
Tommy
ee8e022ccf stop version error when updating and option to disable builtin plugin. (#939) 2017-12-03 12:41:22 -05:00
Zachary Yedidia
3ca55f77a6 Merge 2017-12-01 20:39:30 -05:00
Zachary Yedidia
5f304db4a1 Update readme 2017-12-01 20:39:25 -05:00
Petr Shevtsov
93b8f10b02 Typo (#934) 2017-11-30 11:39:44 -05:00
Zachary Yedidia
bdb699211a Add raw command to view raw terminal esc codes 2017-11-29 01:06:16 -05:00
Zachary Yedidia
acd42df13c Fix panic on scroll
Fixes #932
2017-11-27 21:44:29 -05:00
Ayman Nedjmeddine
4365b66398 Add a table of contents in the README 2017-10-10 19:37:28 +01:00
54 changed files with 1441 additions and 1581 deletions

6
.gitmodules vendored
View File

@@ -55,3 +55,9 @@
[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

View File

@@ -1164,3 +1164,53 @@ 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.

View File

@@ -20,6 +20,22 @@ 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
@@ -30,6 +46,7 @@ You can also check out the website for Micro at https://micro-editor.github.io.
* 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)

View File

@@ -458,6 +458,20 @@ func (v *View) EndOfLine(usePlugin bool) bool {
return true
}
// SelectLine selects the entire current line
func (v *View) SelectLine(usePlugin bool) bool {
if usePlugin && !PreActionCall("SelectLine", v) {
return false
}
v.Cursor.SelectLine()
if usePlugin {
return PostActionCall("SelectLine", v)
}
return true
}
// SelectToStartOfLine selects to the start of the current line
func (v *View) SelectToStartOfLine(usePlugin bool) bool {
if usePlugin && !PreActionCall("SelectToStartOfLine", v) {
@@ -543,6 +557,8 @@ func (v *View) ParagraphNext(usePlugin bool) bool {
return true
}
// Retab changes all tabs to spaces or all spaces to tabs depending
// on the user's settings
func (v *View) Retab(usePlugin bool) bool {
if usePlugin && !PreActionCall("Retab", v) {
return false
@@ -679,10 +695,14 @@ func (v *View) InsertNewline(usePlugin bool) bool {
}
ws := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
cx := v.Cursor.X
v.Buf.Insert(v.Cursor.Loc, "\n")
// v.Cursor.Right()
if v.Buf.Settings["autoindent"].(bool) {
if cx < len(ws) {
ws = ws[0:cx]
}
v.Buf.Insert(v.Cursor.Loc, ws)
// for i := 0; i < len(ws); i++ {
// v.Cursor.Right()
@@ -809,13 +829,15 @@ func (v *View) IndentSelection(usePlugin bool) bool {
end := v.Cursor.CurSelection[1]
if end.Y < start.Y {
start, end = end, start
v.Cursor.SetSelectionStart(start)
v.Cursor.SetSelectionEnd(end)
}
startY := start.Y
endY := end.Move(-1, v.Buf).Y
endX := end.Move(-1, v.Buf).X
tabsize := len(v.Buf.IndentString())
for y := startY; y <= endY; y++ {
tabsize := len(v.Buf.IndentString())
v.Buf.Insert(Loc{0, y}, v.Buf.IndentString())
if y == startY && start.X > 0 {
v.Cursor.SetSelectionStart(start.Move(tabsize, v.Buf))
@@ -869,6 +891,8 @@ func (v *View) OutdentSelection(usePlugin bool) bool {
end := v.Cursor.CurSelection[1]
if end.Y < start.Y {
start, end = end, start
v.Cursor.SetSelectionStart(start)
v.Cursor.SetSelectionEnd(end)
}
startY := start.Y
@@ -941,7 +965,7 @@ func (v *View) Save(usePlugin bool) bool {
return false
}
if v.Type.scratch == true {
if v.Type.Scratch == true {
// We can't save any view type with scratch set. eg help and log text
return false
}
@@ -1351,6 +1375,27 @@ func (v *View) PastePrimary(usePlugin bool) bool {
return true
}
// JumpToMatchingBrace moves the cursor to the matching brace if it is
// currently on a brace
func (v *View) JumpToMatchingBrace(usePlugin bool) bool {
if usePlugin && !PreActionCall("JumpToMatchingBrace", v) {
return false
}
for _, bp := range bracePairs {
r := v.Cursor.RuneUnder(v.Cursor.X)
if r == bp[0] || r == bp[1] {
matchingBrace := v.Buf.FindMatchingBrace(bp, v.Cursor.Loc)
v.Cursor.GotoLoc(matchingBrace)
}
}
if usePlugin {
return PostActionCall("JumpToMatchingBrace", v)
}
return true
}
// SelectAll selects the entire buffer
func (v *View) SelectAll(usePlugin bool) bool {
if usePlugin && !PreActionCall("SelectAll", v) {
@@ -1578,21 +1623,38 @@ func (v *View) JumpLine(usePlugin bool) bool {
}
// Prompt for line number
message := fmt.Sprintf("Jump to line (1 - %v) # ", v.Buf.NumLines)
linestring, canceled := messenger.Prompt(message, "", "LineNumber", NoCompletion)
message := fmt.Sprintf("Jump to line:col (1 - %v) # ", v.Buf.NumLines)
input, canceled := messenger.Prompt(message, "", "LineNumber", NoCompletion)
if canceled {
return false
}
lineint, err := strconv.Atoi(linestring)
lineint = lineint - 1 // fix offset
if err != nil {
messenger.Error(err) // return errors
return false
var lineInt int
var colInt int
var err error
if strings.Contains(input, ":") {
split := strings.Split(input, ":")
lineInt, err = strconv.Atoi(split[0])
if err != nil {
messenger.Message("Invalid line number")
return false
}
colInt, err = strconv.Atoi(split[1])
if err != nil {
messenger.Message("Invalid column number")
return false
}
} else {
lineInt, err = strconv.Atoi(input)
if err != nil {
messenger.Message("Invalid line number")
return false
}
}
lineInt--
// Move cursor and view if possible.
if lineint < v.Buf.NumLines && lineint >= 0 {
v.Cursor.X = 0
v.Cursor.Y = lineint
if lineInt < v.Buf.NumLines && lineInt >= 0 {
v.Cursor.X = colInt
v.Cursor.Y = lineInt
if usePlugin {
return PostActionCall("JumpLine", v)
@@ -1697,6 +1759,22 @@ func (v *View) CommandMode(usePlugin bool) bool {
return false
}
// ToggleOverwriteMode lets the user toggle the text overwrite mode
func (v *View) ToggleOverwriteMode(usePlugin bool) bool {
if v.mainCursor() {
if usePlugin && !PreActionCall("ToggleOverwriteMode", v) {
return false
}
v.isOverwriteMode = !v.isOverwriteMode
if usePlugin {
return PostActionCall("ToggleOverwriteMode", v)
}
}
return false
}
// Escape leaves current mode
func (v *View) Escape(usePlugin bool) bool {
if v.mainCursor() {

View File

@@ -1,9 +1,9 @@
// +build android plan9 nacl windows
// +build plan9 nacl windows
package main
func (v *View) Suspend(usePlugin bool) bool {
messenger.Error("Suspend is only supported on Linux")
messenger.Error("Suspend is only supported on Posix")
return false
}

View File

@@ -142,6 +142,7 @@ 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
@@ -219,6 +220,7 @@ 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) {
@@ -232,6 +234,7 @@ 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) {

View File

@@ -1,14 +1,17 @@
package main
import (
"fmt"
"io/ioutil"
"os"
"strings"
"unicode"
"github.com/flynn/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
@@ -40,6 +43,7 @@ var bindingActions = map[string]func(*View, bool) bool{
"SelectWordLeft": (*View).SelectWordLeft,
"DeleteWordRight": (*View).DeleteWordRight,
"DeleteWordLeft": (*View).DeleteWordLeft,
"SelectLine": (*View).SelectLine,
"SelectToStartOfLine": (*View).SelectToStartOfLine,
"SelectToEndOfLine": (*View).SelectToEndOfLine,
"ParagraphPrevious": (*View).ParagraphPrevious,
@@ -87,6 +91,7 @@ var bindingActions = map[string]func(*View, bool) bool{
"ClearStatus": (*View).ClearStatus,
"ShellMode": (*View).ShellMode,
"CommandMode": (*View).CommandMode,
"ToggleOverwriteMode": (*View).ToggleOverwriteMode,
"Escape": (*View).Escape,
"Quit": (*View).Quit,
"QuitAll": (*View).QuitAll,
@@ -107,6 +112,7 @@ var bindingActions = map[string]func(*View, bool) bool{
"RemoveMultiCursor": (*View).RemoveMultiCursor,
"RemoveAllMultiCursors": (*View).RemoveAllMultiCursors,
"SkipMultiCursor": (*View).SkipMultiCursor,
"JumpToMatchingBrace": (*View).JumpToMatchingBrace,
// This was changed to InsertNewline but I don't want to break backwards compatibility
"InsertEnter": (*View).InsertNewline,
@@ -266,6 +272,7 @@ type Key struct {
// 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
@@ -335,6 +342,7 @@ 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{
@@ -400,6 +408,43 @@ func findMouseAction(v string) func(*View, bool, *tcell.EventMouse) bool {
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)
@@ -424,6 +469,7 @@ func BindKey(k, v string) {
if actionNames[0] == "UnbindKey" {
delete(bindings, key)
delete(mouseBindings, key)
delete(bindingsStr, k)
if len(actionNames) == 1 {
return
}
@@ -434,6 +480,12 @@ func BindKey(k, v string) {
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))
}
@@ -443,6 +495,7 @@ func BindKey(k, v string) {
// 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)
@@ -521,6 +574,7 @@ func DefaultBindings() map[string]string {
"CtrlW": "NextSplit",
"CtrlU": "ToggleMacro",
"CtrlJ": "PlayMacro",
"Insert": "ToggleOverwriteMode",
// Emacs-style keybindings
"Alt-f": "WordRight",

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"crypto/md5"
"encoding/gob"
"errors"
"io"
"io/ioutil"
"os"
@@ -52,6 +53,7 @@ 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
@@ -72,6 +74,8 @@ 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)
}
@@ -158,7 +162,7 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
InitLocalSettings(b)
if b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool) {
if len(*flagStartPos) == 0 && (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))
@@ -201,6 +205,8 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
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 == "" {
@@ -333,6 +339,8 @@ 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++ {
@@ -359,6 +367,7 @@ func (b *Buffer) MergeCursors() {
}
}
// UpdateCursors updates all the cursors indicies
func (b *Buffer) UpdateCursors() {
for i, c := range b.cursors {
c.Num = i
@@ -415,17 +424,60 @@ func (b *Buffer) SaveAs(filename string) error {
b.Insert(end, "\n")
}
}
str := b.SaveString(b.Settings["fileformat"] == "dos")
data := []byte(str)
err := ioutil.WriteFile(ReplaceHome(filename), data, 0644)
if err == nil {
b.Path = filename
b.IsModified = false
defer func() {
b.ModTime, _ = GetModTime(filename)
return b.Serialize()
}()
// 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")
}
}
}
b.ModTime, _ = GetModTime(filename)
return err
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()
}
// SaveAsWithSudo is the same as SaveAs except it uses a neat trick
@@ -466,6 +518,8 @@ 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
@@ -517,6 +571,7 @@ 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)
}
@@ -597,3 +652,58 @@ func (b *Buffer) clearCursors() {
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
}

View File

@@ -69,6 +69,17 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
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))
@@ -137,7 +148,13 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
char := line[colN]
if viewCol >= 0 {
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, char, curStyle, 1}
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}
}
}
if char == '\t' {
charWidth := tabsize - (viewCol+left)%tabsize
@@ -146,7 +163,8 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
c.lines[viewLine][viewCol].width = charWidth
indentStyle := curStyle
if group, ok := colorscheme["indent-char"]; ok {
ch := buf.Settings["indentchar"].(string)
if group, ok := colorscheme["indent-char"]; ok && !IsStrWhitespace(ch) && ch != "" {
indentStyle = group
}
@@ -155,7 +173,7 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
for i := 1; i < charWidth; i++ {
viewCol++
if viewCol >= 0 && viewCol < lineLength {
if viewCol >= 0 && viewCol < lineLength && viewCol < len(c.lines[viewLine]) {
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
}
}
@@ -167,7 +185,7 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
}
for i := 1; i < charWidth; i++ {
viewCol++
if viewCol >= 0 && viewCol < lineLength {
if viewCol >= 0 && viewCol < lineLength && viewCol < len(c.lines[viewLine]) {
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
}
}

View File

@@ -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,5 +252,9 @@ func GetColor256(color int) tcell.Color {
tcell.Color253, tcell.Color254, tcell.Color255,
}
return colors[color]
if color >= 0 && color < len(colors) {
return colors[color]
}
return tcell.ColorDefault
}

View File

@@ -1,11 +1,8 @@
package main
import (
"bytes"
"fmt"
"os"
"os/exec"
"os/signal"
"path/filepath"
"regexp"
"runtime"
@@ -37,6 +34,7 @@ func init() {
"Set": Set,
"SetLocal": SetLocal,
"Show": Show,
"ShowKey": ShowKey,
"Run": Run,
"Bind": Bind,
"Quit": Quit,
@@ -55,8 +53,10 @@ func init() {
"Pwd": Pwd,
"Open": Open,
"TabSwitch": TabSwitch,
"Term": Term,
"MemUsage": MemUsage,
"Retab": Retab,
"Raw": Raw,
}
}
@@ -93,6 +93,7 @@ func DefaultCommands() 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}},
@@ -111,8 +112,32 @@ func DefaultCommands() map[string]StrCommand {
"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
}
}
@@ -199,10 +224,34 @@ 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 {
@@ -489,6 +538,20 @@ 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 {
@@ -644,94 +707,17 @@ func ReplaceAll(args []string) {
Replace(append(args, "-a"))
}
// 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]
cmd := exec.Command(inputCmd, args[1:]...)
outputBytes := &bytes.Buffer{}
cmd.Stdout = outputBytes
cmd.Stderr = outputBytes
cmd.Start()
err = cmd.Wait() // wait for command to finish
outstring := outputBytes.String()
return outstring, 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 {
args, err := shellwords.Split(input)
if err != nil {
return ""
}
inputCmd := args[0]
if !openTerm {
// Simply run the command in the background and notify the user when it's done
messenger.Message("Running...")
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()
}()
// 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, "")
} else {
// 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
var output string
cmd := exec.Command(inputCmd, args...)
cmd.Stdin = os.Stdin
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()
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
err = CurView().StartTerminal(args, true, false, "")
}
if err != nil {
messenger.Error(err)
}
return ""
}
// HandleCommand handles input from the user

View File

@@ -35,6 +35,13 @@ func (c *Cursor) Goto(b Cursor) {
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"
func (c *Cursor) CopySelection(target string) {

View File

@@ -28,6 +28,7 @@ type TextEvent struct {
Time time.Time
}
// A Delta is a change to the buffer
type Delta struct {
Text string
Start Loc

View File

@@ -29,6 +29,8 @@ 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
@@ -43,10 +45,12 @@ 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)
@@ -243,18 +247,22 @@ 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
}

View File

@@ -416,7 +416,6 @@ func importFilePath() *lua.LTable {
pkg := L.NewTable()
L.SetField(pkg, "Join", luar.New(L, filepath.Join))
L.SetField(pkg, "Clean", 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))

View File

@@ -223,6 +223,7 @@ func (m *Messenger) LetterPrompt(prompt string, responses ...rune) (rune, bool)
}
}
// Completion represents a type of completion
type Completion int
const (
@@ -322,7 +323,7 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
chosen = chosen + CommonSubstring(suggestions...)
}
if chosen != "" {
if len(suggestions) != 0 && chosen != "" {
m.response = shellwords.Join(append(args[:len(args)-1], chosen)...)
m.cursorx = Count(m.response)
}
@@ -348,6 +349,7 @@ 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--
@@ -355,6 +357,8 @@ func (m *Messenger) UpHistory(history []string) {
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++
@@ -362,33 +366,47 @@ func (m *Messenger) DownHistory(history []string) {
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()
@@ -410,6 +428,8 @@ func (m *Messenger) WordLeft() {
}
m.CursorRight()
}
// WordRight moves the cursor one word to the right
func (m *Messenger) WordRight() {
response := []rune(m.response)
if m.cursorx >= len(response) {
@@ -433,6 +453,8 @@ func (m *Messenger) WordRight() {
}
}
}
// DeleteWordLeft deletes one word to the left
func (m *Messenger) DeleteWordLeft() {
m.WordLeft()
m.response = string([]rune(m.response)[:m.cursorx])
@@ -606,7 +628,7 @@ func (m *Messenger) SaveHistory() {
// Don't save history past 100
for k, v := range m.history {
if len(v) > 100 {
m.history[k] = v[0:100]
m.history[k] = v[len(m.history[k])-100:]
}
}

View File

@@ -7,7 +7,6 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/go-errors/errors"
@@ -44,8 +43,10 @@ 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 = "Unknown"
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
CompileDate = "Unknown"
// The list of views
@@ -57,8 +58,10 @@ var (
// Channel of jobs running in the background
jobs chan JobFunction
// Event channel
events chan tcell.Event
autosave chan bool
events chan tcell.Event
autosave chan bool
updateterm chan bool
closeterm chan int
)
// LoadInput determines which files should be loaded into buffers
@@ -206,7 +209,7 @@ func InitScreen() {
screen.EnableMouse()
}
screen.SetStyle(defStyle)
// screen.SetStyle(defStyle)
}
// RedrawAll redraws everything -- all the views and the messenger
@@ -253,7 +256,7 @@ func LoadAll() {
}
}
// Passing -version as a flag will have micro print out the version number
// Command line flags
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")
@@ -271,7 +274,7 @@ func main() {
fmt.Println("-version")
fmt.Println(" \tShow the version number and information")
fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the bindings.json\nfile (see 'help options').\n\n")
fmt.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`")
@@ -387,6 +390,12 @@ 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))
@@ -423,22 +432,20 @@ func main() {
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 {
for pl := range loadedPlugins {
_, err := Call(pl+".onViewOpen", v)
if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
TermMessage(err)
continue
}
}
GlobalPluginCall("onViewOpen", v)
GlobalPluginCall("onBufferOpen", v.Buf)
}
}
InitColorscheme()
messenger.style = defStyle
// Here is the event loop which runs in a separate thread
go func() {
@@ -471,7 +478,13 @@ func main() {
f.function(f.output, f.args...)
continue
case <-autosave:
CurView().Save(true)
if CurView().Buf.Path != "" {
CurView().Save(true)
}
case <-updateterm:
continue
case vnum := <-closeterm:
tabs[curTab].views[vnum].CloseTerminal()
case event = <-events:
}
@@ -515,8 +528,10 @@ func main() {
view = tabs[curTab].views[v.Num]
}
}
view.HandleEvent(e)
didAction = true
if view != nil {
view.HandleEvent(e)
didAction = true
}
}
}
}

View File

@@ -61,6 +61,10 @@ 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)
@@ -114,10 +118,13 @@ 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 {
if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
TermMessage(err)
}
}
@@ -163,3 +170,15 @@ func LoadPlugins() {
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
}
}
}

View File

@@ -8,7 +8,6 @@ import (
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"sort"
"strings"
@@ -423,6 +422,7 @@ func (pv *PluginVersion) DownloadAndInstall() error {
}
}
// Install files and directory's
for _, f := range z.File {
parts := strings.Split(f.Name, "/")
if allPrefixed {
@@ -435,7 +435,7 @@ func (pv *PluginVersion) DownloadAndInstall() error {
return err
}
} else {
basepath := path.Dir(targetName)
basepath := filepath.Dir(targetName)
if err := os.MkdirAll(basepath, dirPerm); err != nil {
return err

File diff suppressed because one or more lines are too long

View File

@@ -1,15 +1,17 @@
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)
screen.SetContent(sb.view.x+sb.view.Width-1, sb.view.y+sb.pos(), ' ', nil, style)
}
func (sb *ScrollBar) Pos() int {
func (sb *ScrollBar) pos() int {
numlines := sb.view.Buf.NumLines
h := sb.view.Height
filepercent := float32(sb.view.Topline) / float32(numlines)

View File

@@ -100,15 +100,23 @@ func InitLocalSettings(buf *Buffer) {
for k, v := range parsed {
if strings.HasPrefix(reflect.TypeOf(v).String(), "map") {
g, err := glob.Compile(k)
if err != nil {
TermMessage("Error with glob setting ", k, ": ", err)
continue
}
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
}
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
}
}
}
}
@@ -194,6 +202,7 @@ func DefaultGlobalSettings() map[string]interface{} {
return map[string]interface{}{
"autoindent": true,
"autosave": false,
"basename": false,
"colorcolumn": float64(0),
"colorscheme": "default",
"cursorline": true,
@@ -205,6 +214,7 @@ func DefaultGlobalSettings() map[string]interface{} {
"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{},
@@ -236,6 +246,7 @@ func DefaultLocalSettings() map[string]interface{} {
return map[string]interface{}{
"autoindent": true,
"autosave": false,
"basename": false,
"colorcolumn": float64(0),
"cursorline": true,
"eofnewline": false,
@@ -245,6 +256,7 @@ func DefaultLocalSettings() map[string]interface{} {
"ignorecase": false,
"indentchar": " ",
"keepautoindent": false,
"matchbrace": false,
"rmtrailingws": false,
"ruler": true,
"savecursor": false,

129
cmd/micro/shell.go Normal file
View File

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

View File

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

View File

@@ -0,0 +1,11 @@
// +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")
}

View File

@@ -168,19 +168,10 @@ func Join(args ...string) string {
for _, b := range w {
switch b {
case 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u',
'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'_', '-', '.', ',', ':', '/', '@':
case ' ', '\t', '\r', '\n':
buf.WriteByte('\\')
buf.WriteString(string(b))
default:
buf.WriteByte('\\')
buf.WriteString(string(b))
}
}

View File

@@ -1,6 +1,7 @@
package main
import (
"path"
"strconv"
)
@@ -22,6 +23,9 @@ func (sline *Statusline) Display() {
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() {
@@ -69,6 +73,12 @@ 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)

View File

@@ -8,6 +8,8 @@ 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
@@ -53,10 +55,13 @@ 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
@@ -73,6 +78,9 @@ func (t *Tab) Resize() {
for i, v := range t.views {
v.Num = i
if v.Type == vtTerm {
v.term.Resize(v.Width, v.Height)
}
}
}

228
cmd/micro/terminal.go Normal file
View File

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

View File

@@ -1,7 +1,9 @@
package main
import (
"fmt"
"os"
"reflect"
"strconv"
"strings"
"time"
@@ -11,9 +13,9 @@ import (
// 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 (
@@ -21,6 +23,8 @@ 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.
@@ -70,6 +74,8 @@ 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
@@ -89,11 +95,16 @@ 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
@@ -131,6 +142,8 @@ 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") {
@@ -151,6 +164,24 @@ 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 {
@@ -242,7 +273,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
@@ -358,6 +395,10 @@ 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
@@ -429,6 +470,31 @@ 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) {
@@ -444,7 +510,6 @@ 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))
}
@@ -460,7 +525,7 @@ func (v *View) ExecuteActions(actions []func(*View, bool) bool) bool {
for _, action := range actions {
readonlyBindingsResult := false
funcName := ShortFuncName(action)
if v.Type.readonly == true {
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) {
@@ -496,6 +561,25 @@ func (v *View) SetCursor(c *Cursor) bool {
// 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
@@ -506,7 +590,7 @@ func (v *View) HandleEvent(event tcell.Event) {
case *tcell.EventRaw:
for key, actions := range bindings {
if key.keyCode == -1 {
if e.EscapeCode() == key.escape {
if e.EscSeq() == key.escape {
for _, c := range v.Buf.cursors {
ok := v.SetCursor(c)
if !ok {
@@ -547,9 +631,10 @@ func (v *View) HandleEvent(event tcell.Event) {
}
}
}
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 {
if v.Type.Readonly == false {
for _, c := range v.Buf.cursors {
v.SetCursor(c)
@@ -558,7 +643,14 @@ func (v *View) HandleEvent(event tcell.Event) {
v.Cursor.DeleteSelection()
v.Cursor.ResetSelection()
}
v.Buf.Insert(v.Cursor.Loc, string(e.Rune()))
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)
@@ -576,7 +668,7 @@ func (v *View) HandleEvent(event tcell.Event) {
}
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
}
@@ -708,12 +800,17 @@ 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 {
// Log views should always follow the cursor...
if v.Type == vtLog || v.Type == vtRaw {
// Log or raw views should always follow the cursor...
v.Relocate()
}
@@ -777,11 +874,11 @@ func (v *View) DisplayView() {
}
colorcolumn := int(v.Buf.Settings["colorcolumn"].(float64))
if colorcolumn != 0 {
if colorcolumn != 0 && xOffset+colorcolumn-v.leftCol < v.Width {
style := GetColor("color-column")
fg, _, _ := style.Decompose()
st := defStyle.Background(fg)
screen.SetContent(xOffset+colorcolumn, yOffset+visualLineN, ' ', nil, st)
screen.SetContent(xOffset+colorcolumn-v.leftCol, yOffset+visualLineN, ' ', nil, st)
}
screenX = v.x

View File

@@ -0,0 +1,26 @@
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"

View File

@@ -10,60 +10,44 @@ This help page aims to cover two aspects of micro's syntax highlighting engine:
Micro comes with a number of colorschemes by default. Here is the list:
* simple: this is the simplest colorscheme. It uses 16 colors which are set by
your terminal
### 256 color
* 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
These should work and look nice in most terminals. I recommend these
themes the most.
* `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.
* atom-dark-tc: this colorscheme is based off of Atom's "dark" colorscheme. It
requires true color to look good.
* cmc-16: A very nice 16-color theme. Written by contributor CaptainMcClellan
(Collin Warren.) Licensed under the same license as the rest of the themes.
* cmc-paper: Basically cmc-16, but on a white background. (Actually light grey
* `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.
* cmc-tc: A true colour variant of the cmc theme. It requires true color to
### True color
These require terminals that support true color and require `MICRO_TRUECOLOR=1` (this is an environment variable).
* `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.
* 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
* `gruvbox-tc`: The true color version of the gruvbox colorscheme
* `github-tc`: The true color version of the Github colorscheme
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

View File

@@ -80,6 +80,16 @@ Here are the possible commands that you can use.
* `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:

View File

@@ -5,16 +5,18 @@ 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.
If you want to see all the keybindings press CtrlE and type `help keybindings`.
For a list of the default keybindings press CtrlE and type `help defaultkeys`.
For more information on keybindings see `> help keybindings`.
See the next section for more information about documentation and help.
## Quick-start
Press CtrlQ to quit, and CtrlS to save. Press CtrlE to start typing commands and
you can see which commands are available by pressing tab, or by viewing the help
topic `> help commands`. When I write `> ...` I mean press Ctrl0E and then type
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

View File

@@ -47,6 +47,33 @@ 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
@@ -129,6 +156,7 @@ MoveLinesUp
MoveLinesDown
DeleteWordRight
DeleteWordLeft
SelectLine
SelectToStartOfLine
SelectToEndOfLine
InsertNewline
@@ -183,7 +211,7 @@ HSplit
PreviousSplit
ToggleMacro
PlayMacro
Suspend (Linux only)
Suspend (Unix only)
ScrollUp
ScrollDown
SpawnMultiCursor
@@ -191,6 +219,7 @@ RemoveMultiCursor
RemoveAllMultiCursors
SkipMultiCursor
UnbindKey
JumpToMatchingBrace
```
You can also bind some mouse actions (these must be bound to mouse buttons)

View File

@@ -11,14 +11,19 @@ Here are the options that you can set:
* `autoindent`: when creating a new line use the same indentation as the
previous line.
default value: `on`
default value: `true`
* `autosave`: micro will save the buffer every 8 seconds automatically. Micro
also will automatically save and quit when you exit without asking. Be
careful when using this feature, because you might accidentally save a file,
overwriting what was there before.
default value: `off`
default value: `false`
* `basename`: in the infobar, show only the basename of the file being edited
rather than the full path.
default value: `false`
* `colorcolumn`: if this is not set to 0, it will display a column at the
specified column. This is useful if you want column 80 to be highlighted
@@ -44,7 +49,7 @@ Here are the options that you can set:
* `cursorline`: highlight the line that the cursor is on in a different color
(the color is defined by the colorscheme you are using).
default value: `on`
default value: `true`
* `eofnewline`: micro will automatically add a newline to the file.
@@ -59,7 +64,7 @@ Here are the options that you can set:
intensive. This option is only for people who really care about having
accurate modified status.
default value: `on`
default value: `true`
* `fileformat`: this determines what kind of line endings micro will use for the
file. UNIX line endings are just `\n` (lf) whereas dos line endings are
@@ -78,7 +83,7 @@ Here are the options that you can set:
* `ignorecase`: perform case-insensitive searches.
default value: `off`
default value: `false`
* `indentchar`: sets the indentation character.
@@ -87,20 +92,20 @@ Here are the options that you can set:
* `infobar`: enables the line at the bottom of the editor where messages are
printed. This option is `global only`.
default value: `on`
default value: `true`
* `keepautoindent`: when using autoindent, whitespace is added for you. This
option determines if when you move to the next line without any insertions
the whitespace that was added should be deleted. By default the autoindent
whitespace is deleted if the line was left empty.
default value: `off`
default value: `false`
* `keymenu`: display the nano-style key menu at the bottom of the screen. Note
that ToggleKeyMenu is bound to `Alt-g` by default and this is displayed in
the statusline. To disable this, simply by `Alt-g` to `UnbindKey`.
default value: `off`
default value: `false`
* `mouse`: whether to enable mouse support. When mouse support is disabled,
usually the terminal will be able to access mouse events which can be useful
@@ -108,7 +113,7 @@ Here are the options that you can set:
example, because the terminal has access to the local clipboard and micro
does not).
default value: `on`
default value: `true`
* `pluginchannels`: contains all the channels micro's plugin manager will search
for plugins in. A channel is simply a list of 'repository' json files which
@@ -129,26 +134,26 @@ Here are the options that you can set:
* `ruler`: display line numbers.
default value: `on`
default value: `true`
* `savecursor`: remember where the cursor was last time the file was opened and
put it there when you open the file again.
default value: `off`
default value: `false`
* `savehistory`: remember command history between closing and re-opening
micro.
default value: `on`
default value: `true`
* `saveundo`: when this option is on, undo is saved even after you close a file
so if you close and reopen a file, you can keep undoing.
default value: `off`
default value: `false`
* `scrollbar`: display a scroll bar
default value: `off`
default value: `false`
* `scrollmargin`: amount of lines you would like to see above and below the
cursor.
@@ -161,25 +166,29 @@ Here are the options that you can set:
* `softwrap`: should micro wrap lines that are too long to fit on the screen.
default value: `off`
default value: `false`
* `splitbottom`: when a horizontal split is created, should it be created below
the current split?
default value: `on`
default value: `true`
* `splitright`: when a vertical split is created, should it be created to the
right of the current split?
default value: `on`
default value: `true`
* `statusline`: display the status line at the bottom of the screen.
default value: `on`
default value: `true`
* `matchbrace`: highlight matching braces for '()', '{}', '[]'
default value: `false`
* `syntax`: turns syntax on or off.
default value: `on`
default value: `true`
* `sucmd`: specifies the super user command. On most systems this is "sudo" but
on BSD it can be "doas." This option can be customized and is only used when
@@ -191,7 +200,7 @@ Here are the options that you can set:
(e.g. move over 4 spaces at once). This option only does anything if
`tabstospaces` is on.
default value: `off`
default value: `false`
* `tabsize`: sets the tab size to `option`
@@ -199,18 +208,18 @@ Here are the options that you can set:
* `tabstospaces`: use spaces instead of tabs
default value: `off`
default value: `false`
* `termtitle`: defines whether or not your terminal's title will be set by micro
when opened.
default value: `off`
default value: `false`
* `useprimary` (only useful on *nix): defines whether or not micro will use the
primary clipboard to copy selections in the background. This does not affect
the normal clipboard using Ctrl-C and Ctrl-V.
default value: `on`
default value: `true`
---
@@ -219,19 +228,19 @@ Default plugin options:
* `autoclose`: automatically close `{}` `()` `[]` `""` `''`. Provided by the
`autoclose` plugin
default value: `on`
default value: `true`
* `ftoptions`: by default, micro will set some options based on the filetype. At
the moment, micro will use tabs for makefiles and spaces for python and yaml
files regardless of your settings. If you would like to disable this behavior
turn this option off.
default value: `on`
default value: `true`
* `linter`: Automatically lint when the file is saved. Provided by the `linter`
plugin.
default value: `on`
default value: `true`
Any option you set in the editor will be saved to the file
~/.config/micro/settings.json so, in effect, your configuration file will be
@@ -249,10 +258,25 @@ will set the option in all buffers.
The `colorscheme` option is global only, and the `filetype` option is local
only. To set an option locally, use `setlocal` instead of `set`.
In the `settings.json` file you can also put set options locally by specifying a
glob. Here is an example which has `tabstospaces` on for all files except Go
In the `settings.json` file you can also put set options locally by specifying either
a glob or a filetype. Here is an example which has `tabstospaces` on for all files except Go
files, and `tabsize` 4 for all files except Ruby files:
```json
{
"ft:go": {
"tabstospaces": false
},
"ft:ruby": {
"tabsize": 2
},
"tabstospaces": true,
"tabsize": 4
}
```
Or similarly you can match with globs:
```json
{
"*.go": {
@@ -265,6 +289,3 @@ files, and `tabsize` 4 for all files except Ruby files:
"tabsize": 4
}
```
As you can see it is quite easy to set options locally using the `settings.json`
file.

View File

@@ -108,11 +108,33 @@ functions are given using Go's type system):
* `HandleCommand(cmd string)`: runs the given command
* `HandleShellCommand(shellCmd string, interactive bool, waitToClose bool)`:
runs the given shell command. The `interactive` bool specifies whether the
command should run in the background. The `waitToClose` bool only applies if
`interactive` is true and means that it should wait before returning to the
editor.
* `ExecCommand(name string, args []string) (string, error)`: exec a (shell) command with the
given arguments. Returns the command's output and a possible error.
* `RunShellCommand(cmd string) (string, error)`: Run a shell command. This uses `ExecCommand`
under the hood but also does some parsing for the arguments (i.e. quoted arguments). The
function returns the command's output and a possible error.
* `RunBackgroundShell(cmd string)`: Run a shell command in the background.
* `RunInteractiveShell(cmd string, wait bool, getOutput bool) (string, error)`: Run a shell command
by closing micro and running the command interactively. If `wait` is true, a prompt will be
used after the process exits to prevent the terminal from immediately returning to micro, allowing
the user to view the output of the process. If `getOutput` is true, the command's standard output
will be returned. Note that if `getOutput` is true, some interactive commands may not behave
normally because `isatty` will return false.
* `RunTermEmulator(cmd string, wait bool, getOutput bool, callback string) error`: Same as
`RunInteractiveShell` except the command is run within the current split in a terminal emulator.
The `callback` input is a string callback to a lua function which will be called when the process
exits. The output of the process will be provided as the first and only argument to the callback
(it will be empty if `getOutput` is false).
Note that this functionality is only supported on some operating systems (linux, darwin, dragonfly,
openbsd, freebsd). Use the `TermEmuSupported` (see below) boolean to determine if the current
system is supported.
* `TermEmuSupported`: Boolean specifying if the terminal emulator is supported on the version of
micro that is running.
* `ToCharPos(loc Loc, buf *Buffer) int`: returns the character position of a
given x, y location

View File

@@ -6,10 +6,12 @@ configure micro to your liking.
Hopefully you'll find this useful.
See `> help defaultkeys` for a list an explanation of the default keybindings.
### Plugins
Micro has a plugin manager which can automatically download plugins for you. To
see the plugins 'official' plugins, go to github.com/micro-editor/plugin-channel.
see the 'official' plugins, go to github.com/micro-editor/plugin-channel.
For example, if you'd like to install the snippets plugin (listed in that repo),
just press `CtrlE` to execute a command, and type `plugin install snippets`.

View File

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

View File

@@ -1,109 +0,0 @@
# utf8.lua
one-file pure-lua 5.1 regex library
This library _is_ the simple way to add utf8 support into your application.
Some examples from http://www.lua.org/manual/5.1/manual.html#5.4 :
```Lua
local utf8 = require "utf8"
local s = "hello world from Lua"
for w in string.gmatch(s, "%a+") do
print(w)
end
--[[
hello
world
from
Lua
]]--
s = "Привет, мир, от Lua"
for w in utf8.gmatch(s, "[^%p%d%s%c]+") do
print(w)
end
--[[
Привет
мир
от
Lua
]]--
local t = {}
s = "from=world, to=Lua"
for k, v in string.gmatch(s, "(%w+)=(%w+)") do
t[k] = v
end
for k,v in pairs(t) do
print(k,v)
end
--[[
to Lua
from world
]]--
t = {}
s = "从=世界, 到=Lua"
for k, v in utf8.gmatch(s, "([^%p%s%c]+)=([^%p%s%c]+)") do
t[k] = v
end
for k,v in pairs(t) do
print(k,v)
end
--[[
到 Lua
从 世界
]]--
local x = string.gsub("hello world", "(%w+)", "%1 %1")
print(x)
--hello hello world world
x = utf8.gsub("Ahoj světe", "([^%p%s%c]+)", "%1 %1")
print(x)
--Ahoj Ahoj světe světe
x = string.gsub("hello world", "%w+", "%0 %0", 1)
print(x)
--hello hello world
x = utf8.gsub("Ahoj světe", "[^%p%s%c]+", "%0 %0", 1)
print(x)
--Ahoj Ahoj světe
x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
print(x)
--world hello Lua from
x = utf8.gsub("γεια κόσμο από Lua", "([^%p%s%c]+)%s*([^%p%s%c]+)", "%2 %1")
print(x)
--κόσμο γεια Lua από
```
Notice, there are some classes that can work only with latin(ASCII) symbols,
for details see: https://github.com/Stepets/utf8.lua/blob/master/utf8.lua#L470
Of course you can do this trick:
```Lua
for k,v in pairs(utf8) do
string[k] = v
end
```
But this can lead to very strange errors. You were warned.
A little bit more interesting examples:
```Lua
local utf8 = require 'utf8'
for k,v in pairs(utf8) do
string[k] = v
end
local str = "пыщпыщ ололоо я водитель нло"
print(str:find("(.л.+)н"))
-- 8 26 ололоо я водитель
print(str:gsub("ло+", "보라"))
-- пыщпыщ о보라보라 я водитель н보라 3
print(str:match("^п[лопыщ ]*я"))
-- пыщпыщ ололоо я
```

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,7 @@
VERSION = "1.0.0"
-- VERSION = "1.0.0"
if GetOption("literate") == nil then
AddOption("literate", true)
end
function startswith(str, start)
return string.sub(str,1,string.len(start))==start
@@ -16,6 +19,7 @@ function split(string, sep)
end
function onViewOpen(view)
if not GetOption("literate") then return end
if not endswith(view.Buf.Path, ".lit") then
return
end

44
runtime/syntax/ada.yaml Normal file
View File

@@ -0,0 +1,44 @@
filetype: ada
detect:
filename: "(\\.ads$|\\.adb$|\\.ada$)"
rules:
# Operators
- symbol.operator: ([.:;,+*|=!?\\%]|<|>|/|-|&)
- symbol.brackets: "[(){}]|\\[|\\]"
# keyword.reserved
- statement.reserved: \b(abort|abs|abstract|accept|access|aliased|all|and|array|at|begin|body|case)\b
- statement.reserved: \b(constant|declare|delay|delta|digits|do|else|elsif|end|entry|exception|exit|for|function)\b
- statement.reserved: \b(generic|goto|if|in|interface|is|limited|loop|mod|new|not|null|of|or|others|out|overriding)\b
- statement.reserved: \b(package|pragma|private|procedure|protected|raise|range|record|rem|renames|return|requeue)\b
- statement.reserved: \b(reverse|select|separate|some|subtype|synchronized|tagged|task|terminate|then|type|until)\b
- statement.reserved: \b(use|when|while|with|xor)\b
# Constant
- constant.bool: \b(TRUE|FALSE)
- constant.number: ([0-9]+)
# Storage Types
- type.storage: \b(INTEGER|NATURAL|POSITIVE|FLOAT|CHARACTER|STRING)\b
- type.storage: \b(LONG_INTEGER|SHORT_INTEGER|LONG_FLOAT|SHORT_FLOAT)\b
#Character
- constant.string.char: \'.\'
# String
- constant.string:
start: \"
end: \"
skip: \\.
rules:
- constant.specialChar: (\\0|\\\\|\\t|\\n|\\r|\\"|\\')
- constant.interpolation: \\\([[:graph:]]*\)
- constant.unicode: \\u\{[[:xdigit:]]+}
# Line Comment
- comment.line: "--.*"
# Todo
- todo: "(TODO|XXX|FIXME):?"

View File

@@ -1,29 +1,25 @@
filetype: git-commit
detect:
filename: "COMMIT_EDITMSG|TAG_EDITMSG"
filename: "^(.*[\\/])?(COMMIT_EDITMSG|TAG_EDITMSG)$"
rules:
# Commit message
- ignore: ".*"
# Comments
- comment:
start: "#"
end: "$"
rules: []
# File changes
- type.keyword: "#[[:space:]](deleted|modified|new file|renamed):[[:space:]].*"
- type.keyword: "#[[:space:]]deleted:"
- type.keyword: "#[[:space:]]modified:"
- type.keyword: "#[[:space:]]new file:"
- type.keyword: "#[[:space:]]renamed:"
# Untracked filenames
- error: "^# [^/?*:;{}\\\\]+\\.[^/?*:;{}\\\\]+$"
- type.keyword: "^#[[:space:]]Changes.*[:]"
- type.keyword: "^#[[:space:]]Your branch and '[^']+"
- type.keyword: "^#[[:space:]]Your branch and '"
- type.keyword: "^#[[:space:]]On branch [^ ]+"
- type.keyword: "^#[[:space:]]On branch"
# Recolor hash symbols
- special: "#"
# Color keywords for closing issues (such as on Github)
- type.keyword: "\\b(?i)((fix(es|ed)?|close(s|d)?) #[0-9]+)\\b"
# Comments
- comment.line:
start: "^#"
end: "$"
rules: []

View File

@@ -1,28 +1,19 @@
filetype: git-rebase-todo
detect:
filename: "git-rebase-todo"
filename: "^(.*[\\/])?git\\-rebase\\-todo$"
rules:
# Rebase commands
- statement: "^(p(ick)?|r(eword)?|e(dit)?|s(quash)?|f(ixup)?|x|exec|d(rop)?)\\b"
# Commit IDs
- identifier: "\\b([0-9a-f]{7,40})\\b"
# Color keywords for Github (and others)
- type.keyword: "\\b(?i)((fix(es|ed)?|close(s|d)?) #[0-9]+)\\b"
# Comments
- comment:
start: "#"
- comment.line:
start: "^#"
end: "$"
rules: []
# Rebase commands
- statement: "^(e|edit) [0-9a-f]{7,40}"
- statement: "^# (e, edit)"
- statement: "^(f|fixup) [0-9a-f]{7,40}"
- statement: "^# (f, fixup)"
- statement: "^(p|pick) [0-9a-f]{7,40}"
- statement: "^# (p, pick)"
- statement: "^(r|reword) [0-9a-f]{7,40}"
- statement: "^# (r, reword)"
- statement: "^(s|squash) [0-9a-f]{7,40}"
- statement: "^# (s, squash)"
- statement: "^(x|exec) [^ ]+ [0-9a-f]{7,40}"
- statement: "^# (x, exec)"
# Recolor hash symbols
- special: "#"
# Commit IDs
- identifier: "[0-9a-f]{7,40}"

View File

@@ -15,16 +15,20 @@ rules:
- symbol.brackets: "(\\{|\\})"
- symbol.brackets: "(\\(|\\))"
- symbol.brackets: "(\\[|\\])"
- statement: "\\b(break|case|catch|continue|default|delete|do|else|finally)\\b"
- statement: "\\b(for|function|class|extends|get|if|in|instanceof|new|return|set|switch|async|await)\\b"
- statement: "\\b(switch|this|throw|try|typeof|var|const|let|void|while|with)\\b"
- symbol.operator: "[-+/*=<>!~%?:&|]"
- statement: "\\b(async|await|break|case|catch|const|continue|debugger|default|delete|do|else|export|finally)\\b"
- statement: "\\b(for|function|class|extends|get|if|import|in|instanceof|let|new|return|set)\\b"
- statement: "\\b(super|switch|this|throw|try|typeof|var|void|while|with|yield)\\b"
# reserved but unassigned
- error: "\\b(enum|implements|interface|package|private|protected|public)"
- constant: "\\b(null|undefined|NaN)\\b"
- constant: "\\b(true|false)\\b"
- type: "\\b(Array|Boolean|Date|Enumerator|Error|Function|Math)\\b"
- type: "\\b(Number|Object|RegExp|String)\\b"
- statement: "[-+/*=<>!~%?:&|]"
- constant: "/[^*]([^/]|(\\\\/))*[^\\\\]/[gim]*"
# - constant: "/[^*]([^/]|(\\\\/))*[^\\\\]/[gim]*"
- constant: "\\\\[0-7][0-7]?[0-7]?|\\\\x[0-9a-fA-F]+|\\\\[bfnrt'\"\\?\\\\]"
- comment: "^#!.*/(env +)?node( |$)"
- constant.string:
start: "\""
@@ -40,6 +44,12 @@ rules:
rules:
- constant.specialChar: "\\\\."
- constant.string:
start: "`"
end: "`"
rules:
- constant.specialChar: "\\\\."
- comment:
start: "//"
end: "$"

View File

@@ -13,7 +13,7 @@ rules:
# definitions
- identifier: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[(]"
# keywords
- statement: "\\b(begin|break|catch|continue|function|elseif|else|end|finally|for|global|local|if|include|using|require|macro|println|return|try|type|while|module)\\b"
- statement: "\\b(begin|break|catch|continue|function|elseif|else|end|finally|for|global|local|const|if|include|using|require|macro|println|return|try|type|while|module)\\b"
# decorators
- identifier.macro: "@[A-Za-z0-9_]+"
# operators

View File

@@ -18,11 +18,12 @@ rules:
- identifier: "coroutine\\.\\b(create|isyieldable|resume|running|status|wrap|yield)\\b"
- identifier: "debug\\.\\b(debug|getfenv|gethook|getinfo|getlocal|getmetatable|getregistry|getupvalue|getuservalue|setfenv|sethook|setlocal|setmetatable|setupvalue|setuservalue|traceback|upvalueid|upvaluejoin)\\b"
- identifier: "bit32\\.\\b(arshift|band|bnot|bor|btest|bxor|extract|replace|lrotate|lshift|rrotate|rshift)\\b"
- identifier: "\\:\\b(close|flush|lines|read|seek|setvbuf|write)\\b"
- identifier: "\\:\\b(close|flush|lines|read|seek|setvbuf|write|byte|char|dump|find|format|gmatch|gsub|len|lower|match|pack|packsize|rep|reverse|sub|unpack|upper)\\b"
- identifier: "\\b(self|arg)\\b"
- constant: "\\b(false|nil|true)\\b"
- statement: "(\\b(dofile|require|include)|%q|%!|%Q|%r|%x)\\b"
- constant.number: "\\b([0-9]+)\\b"
- symbol: "(\\(|\\)|\\[|\\]|\\{|\\}|\\*\\*|\\*|/|%|\\+|-|\\^|>|>=|<|<=|~=|=|\\.\\.|#)"
- symbol: "(\\(|\\)|\\[|\\]|\\{|\\}|\\*\\*|\\*|/|%|\\+|-|\\^|>|>=|<|<=|~=|=|[\\.]{2,3}|#)"
- constant.string:
start: "\""
@@ -46,13 +47,16 @@ rules:
- special: "\\\\[0-7][0-7][0-7]|\\\\x[0-9a-fA-F][0-9a-fA-F]|\\\\[abefnrs]|(\\\\c|\\\\C-|\\\\M-|\\\\M-\\\\C-)."
- comment.block:
start: "\\-\\-\\[(\\=*|\\#*)\\["
end: "\\-\\-\\](\\=*|\\#*)\\]"
rules:
- todo: "(TODO|NOTE|FIXME):?"
# this has to go after block comment or block comment does not work
- comment:
start: "\\-\\-"
end: "$"
rules: []
- comment:
start: "\\-\\-\\[\\["
end: "\\]\\]"
rules: []
rules:
- todo: "(TODO|NOTE|FIXME):?"

View File

@@ -7,8 +7,8 @@ detect:
rules:
- preproc: "\\<(ifeq|ifdef|ifneq|ifndef|else|endif)\\>"
- statement: "^(export|include|override)\\>"
- operator: "^[^:= ]+:"
- operator: "([=,%]|\\+=|\\?=|:=|&&|\\|\\|)"
- symbol.operator: "^[^:= ]+:"
- symbol.operator: "([=,%]|\\+=|\\?=|:=|&&|\\|\\|)"
- statement: "\\$\\((abspath|addprefix|addsuffix|and|basename|call|dir)[[:space:]]"
- statement: "\\$\\((error|eval|filter|filter-out|findstring|firstword)[[:space:]]"
- statement: "\\$\\((flavor|foreach|if|info|join|lastword|notdir|or)[[:space:]]"

View File

@@ -6,13 +6,25 @@ detect:
rules:
- statement: "\\b(syntax|color(-link)?)\\b"
- statement: "\\b(start=|end=)\\b"
- identifier: "\\b(default|comment|symbol|identifier|constant(.string(.char)?|.number)?|statement|preproc|type|special|underlined|error|todo|statusline|indent-char|(current-)?line-number|gutter-error|gutter-warning|cursor-line|color-column)\\b"
# Simple one-liners
- identifier: "\\b(default|number|statement|underlined|error|todo|statusline|indent-char|cursor\\-line|color\\-column|ignore|divider|tabbar)\\b"
# Separate identifiers to keep "complex" regex clean
- identifier: "\\b(special(Char)?)\\b"
- identifier: "\\b((current\\-)?line\\-number)\\b"
- identifier: "\\b(gutter\\-(info|error|warning){1})\\b"
- identifier: "\\b(comment(\\.bright)?)\\b"
- identifier: "\\b(symbol(\\.(brackets|operator|tag))?)\\b"
- identifier: "\\b(identifier(\\.(class|macro|var))?)\\b"
- identifier: "\\b(constant(\\.(bool(\\.(true|false){1})?|number|specialChar|string(\\.url)?){1})?)\\b"
- identifier: "\\b(preproc(\\.shebang)?)\\b"
- identifier: "\\b(type(\\.keyword)?)\\b"
- constant.number: "\\b(|h|A|0x)+[0-9]+(|h|A)+\\b"
- constant.number: "\\b0x[0-9 a-f A-F]+\\b"
- comment:
start: "#"
end: "$"
rules: []
rules:
- todo: "(FIXME|TODO|NOTE):?"
- constant.string:
start: "\""
end: "\""
@@ -20,4 +32,3 @@ rules:
rules:
- constant.specialChar: "\\\\."
- constant.number: "#[0-9 A-F a-f]+"

View File

@@ -24,7 +24,6 @@ type MultiRule struct {
func JoinRule(rule string) string {
split := strings.Split(rule, `" "`)
joined := strings.Join(split, "|")
joined = joined
return joined
}