Merge branch 'master' into fix/file-detection
This commit is contained in:
commit
5bfda7b5f6
65 changed files with 1114 additions and 363 deletions
|
@ -68,6 +68,7 @@ You can also check out the website for Micro at https://micro-editor.github.io.
|
|||
- Small and simple.
|
||||
- Easily configurable.
|
||||
- Macros.
|
||||
- Smart highlighting of trailing whitespace and tab vs space errors.
|
||||
- Common editor features such as undo/redo, line numbers, Unicode support, soft wrapping, …
|
||||
|
||||
## Installation
|
||||
|
|
|
@ -12,5 +12,4 @@ Keywords=text;editor;syntax;terminal;
|
|||
Exec=micro %F
|
||||
StartupNotify=false
|
||||
Terminal=true
|
||||
NoDisplay=true
|
||||
MimeType=text/plain;text/x-chdr;text/x-csrc;text/x-c++hdr;text/x-c++src;text/x-java;text/x-dsrc;text/x-pascal;text/x-perl;text/x-python;application/x-php;application/x-httpd-php3;application/x-httpd-php4;application/x-httpd-php5;application/xml;text/html;text/css;text/x-sql;text/x-diff;
|
||||
|
|
|
@ -449,6 +449,10 @@ func DoEvent() {
|
|||
os.Exit(0)
|
||||
}
|
||||
|
||||
if event == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if e, ok := event.(*tcell.EventError); ok {
|
||||
log.Println("tcell event error: ", e.Error())
|
||||
|
||||
|
@ -469,12 +473,11 @@ func DoEvent() {
|
|||
}
|
||||
|
||||
ulua.Lock.Lock()
|
||||
// if event != nil {
|
||||
if action.InfoBar.HasPrompt {
|
||||
_, resize := event.(*tcell.EventResize)
|
||||
if action.InfoBar.HasPrompt && !resize {
|
||||
action.InfoBar.HandleEvent(event)
|
||||
} else {
|
||||
action.Tabs.HandleEvent(event)
|
||||
}
|
||||
// }
|
||||
ulua.Lock.Unlock()
|
||||
}
|
||||
|
|
|
@ -170,10 +170,19 @@
|
|||
"default": false
|
||||
},
|
||||
"matchbrace": {
|
||||
"description": "Whether to underline matching braces\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||
"description": "Whether to show matching braces\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"matchbracestyle": {
|
||||
"description": "Whether to underline or highlight matching braces\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"underline",
|
||||
"highlight"
|
||||
],
|
||||
"default": "underline"
|
||||
},
|
||||
"mkparents": {
|
||||
"description": "Whether to create missing directories\nhttps://github.com/zyedidia/micro/blob/master/runtime/help/options.md#options",
|
||||
"type": "boolean",
|
||||
|
|
|
@ -51,53 +51,47 @@ func (h *BufPane) ScrollAdjust() {
|
|||
func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
|
||||
b := h.Buf
|
||||
mx, my := e.Position()
|
||||
// ignore click on the status line
|
||||
if my >= h.BufView().Y+h.BufView().Height {
|
||||
return false
|
||||
}
|
||||
mouseLoc := h.LocFromVisual(buffer.Loc{mx, my})
|
||||
h.Cursor.Loc = mouseLoc
|
||||
if h.mouseReleased {
|
||||
if b.NumCursors() > 1 {
|
||||
b.ClearCursors()
|
||||
h.Relocate()
|
||||
h.Cursor = h.Buf.GetActiveCursor()
|
||||
h.Cursor.Loc = mouseLoc
|
||||
}
|
||||
if time.Since(h.lastClickTime)/time.Millisecond < config.DoubleClickThreshold && (mouseLoc.X == h.lastLoc.X && mouseLoc.Y == h.lastLoc.Y) {
|
||||
if h.doubleClick {
|
||||
// Triple click
|
||||
h.lastClickTime = time.Now()
|
||||
|
||||
h.tripleClick = true
|
||||
h.doubleClick = false
|
||||
|
||||
h.Cursor.SelectLine()
|
||||
h.Cursor.CopySelection(clipboard.PrimaryReg)
|
||||
} else {
|
||||
// Double click
|
||||
h.lastClickTime = time.Now()
|
||||
|
||||
h.doubleClick = true
|
||||
h.tripleClick = false
|
||||
|
||||
h.Cursor.SelectWord()
|
||||
h.Cursor.CopySelection(clipboard.PrimaryReg)
|
||||
}
|
||||
} else {
|
||||
h.doubleClick = false
|
||||
h.tripleClick = false
|
||||
if b.NumCursors() > 1 {
|
||||
b.ClearCursors()
|
||||
h.Relocate()
|
||||
h.Cursor = h.Buf.GetActiveCursor()
|
||||
h.Cursor.Loc = mouseLoc
|
||||
}
|
||||
if time.Since(h.lastClickTime)/time.Millisecond < config.DoubleClickThreshold && (mouseLoc.X == h.lastLoc.X && mouseLoc.Y == h.lastLoc.Y) {
|
||||
if h.doubleClick {
|
||||
// Triple click
|
||||
h.lastClickTime = time.Now()
|
||||
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||
h.Cursor.CurSelection[0] = h.Cursor.Loc
|
||||
h.Cursor.CurSelection[1] = h.Cursor.Loc
|
||||
}
|
||||
h.mouseReleased = false
|
||||
} else if !h.mouseReleased {
|
||||
if h.tripleClick {
|
||||
h.Cursor.AddLineToSelection()
|
||||
} else if h.doubleClick {
|
||||
h.Cursor.AddWordToSelection()
|
||||
h.tripleClick = true
|
||||
h.doubleClick = false
|
||||
|
||||
h.Cursor.SelectLine()
|
||||
h.Cursor.CopySelection(clipboard.PrimaryReg)
|
||||
} else {
|
||||
h.Cursor.SetSelectionEnd(h.Cursor.Loc)
|
||||
// Double click
|
||||
h.lastClickTime = time.Now()
|
||||
|
||||
h.doubleClick = true
|
||||
h.tripleClick = false
|
||||
|
||||
h.Cursor.SelectWord()
|
||||
h.Cursor.CopySelection(clipboard.PrimaryReg)
|
||||
}
|
||||
} else {
|
||||
h.doubleClick = false
|
||||
h.tripleClick = false
|
||||
h.lastClickTime = time.Now()
|
||||
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||
h.Cursor.CurSelection[0] = h.Cursor.Loc
|
||||
h.Cursor.CurSelection[1] = h.Cursor.Loc
|
||||
}
|
||||
|
||||
h.Cursor.StoreVisualX()
|
||||
|
@ -106,6 +100,45 @@ func (h *BufPane) MousePress(e *tcell.EventMouse) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (h *BufPane) MouseDrag(e *tcell.EventMouse) bool {
|
||||
mx, my := e.Position()
|
||||
// ignore drag on the status line
|
||||
if my >= h.BufView().Y+h.BufView().Height {
|
||||
return false
|
||||
}
|
||||
h.Cursor.Loc = h.LocFromVisual(buffer.Loc{mx, my})
|
||||
|
||||
if h.tripleClick {
|
||||
h.Cursor.AddLineToSelection()
|
||||
} else if h.doubleClick {
|
||||
h.Cursor.AddWordToSelection()
|
||||
} else {
|
||||
h.Cursor.SetSelectionEnd(h.Cursor.Loc)
|
||||
}
|
||||
|
||||
h.Cursor.StoreVisualX()
|
||||
h.Relocate()
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *BufPane) MouseRelease(e *tcell.EventMouse) bool {
|
||||
// We could finish the selection based on the release location as in the
|
||||
// commented out code below, to allow text selections even in a terminal
|
||||
// that doesn't support mouse motion events. But when the mouse click is
|
||||
// within the scroll margin, that would cause a scroll and selection
|
||||
// even for a simple mouse click, which is not good.
|
||||
// if !h.doubleClick && !h.tripleClick {
|
||||
// mx, my := e.Position()
|
||||
// h.Cursor.Loc = h.LocFromVisual(buffer.Loc{mx, my})
|
||||
// h.Cursor.SetSelectionEnd(h.Cursor.Loc)
|
||||
// }
|
||||
|
||||
if h.Cursor.HasSelection() {
|
||||
h.Cursor.CopySelection(clipboard.PrimaryReg)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ScrollUpAction scrolls the view up
|
||||
func (h *BufPane) ScrollUpAction() bool {
|
||||
h.ScrollUp(util.IntOpt(h.Buf.Settings["scrollspeed"]))
|
||||
|
@ -211,7 +244,7 @@ func (h *BufPane) CursorLeft() bool {
|
|||
func (h *BufPane) CursorRight() bool {
|
||||
if h.Cursor.HasSelection() {
|
||||
h.Cursor.Deselect(false)
|
||||
h.Cursor.Loc = h.Cursor.Loc.Move(1, h.Buf)
|
||||
h.Cursor.Right()
|
||||
} else {
|
||||
tabstospaces := h.Buf.Settings["tabstospaces"].(bool)
|
||||
tabmovement := h.Buf.Settings["tabmovement"].(bool)
|
||||
|
@ -793,25 +826,26 @@ func (h *BufPane) SaveAsCB(action string, callback func()) bool {
|
|||
filename := strings.Join(args, " ")
|
||||
fileinfo, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission) {
|
||||
noPrompt := h.saveBufToFile(filename, action, callback)
|
||||
if noPrompt {
|
||||
h.completeAction(action)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
InfoBar.YNPrompt(
|
||||
fmt.Sprintf("the file %s already exists in the directory, would you like to overwrite? Y/n", fileinfo.Name()),
|
||||
func(yes, canceled bool) {
|
||||
if yes && !canceled {
|
||||
noPrompt := h.saveBufToFile(filename, action, callback)
|
||||
if noPrompt {
|
||||
h.completeAction(action)
|
||||
} else {
|
||||
InfoBar.YNPrompt(
|
||||
fmt.Sprintf("The file %s already exists in the directory, would you like to overwrite? Y/n", fileinfo.Name()),
|
||||
func(yes, canceled bool) {
|
||||
if yes && !canceled {
|
||||
noPrompt := h.saveBufToFile(filename, action, callback)
|
||||
if noPrompt {
|
||||
h.completeAction(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
return false
|
||||
|
@ -1273,9 +1307,13 @@ func (h *BufPane) PastePrimary() bool {
|
|||
|
||||
func (h *BufPane) paste(clip string) {
|
||||
if h.Buf.Settings["smartpaste"].(bool) {
|
||||
if h.Cursor.X > 0 && len(util.GetLeadingWhitespace([]byte(strings.TrimLeft(clip, "\r\n")))) == 0 {
|
||||
leadingWS := util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y))
|
||||
clip = strings.ReplaceAll(clip, "\n", "\n"+string(leadingWS))
|
||||
if h.Cursor.X > 0 {
|
||||
leadingPasteWS := string(util.GetLeadingWhitespace([]byte(clip)))
|
||||
if leadingPasteWS != " " && strings.Contains(clip, "\n"+leadingPasteWS) {
|
||||
leadingWS := string(util.GetLeadingWhitespace(h.Buf.LineBytes(h.Cursor.Y)))
|
||||
clip = strings.TrimPrefix(clip, leadingPasteWS)
|
||||
clip = strings.ReplaceAll(clip, "\n"+leadingPasteWS, "\n"+leadingWS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1711,7 +1749,7 @@ func (h *BufPane) PlayMacro() bool {
|
|||
switch t := action.(type) {
|
||||
case rune:
|
||||
h.DoRuneInsert(t)
|
||||
case func(*BufPane) bool:
|
||||
case BufKeyAction:
|
||||
t(h)
|
||||
}
|
||||
}
|
||||
|
@ -1760,15 +1798,39 @@ func (h *BufPane) SpawnMultiCursor() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// SpawnMultiCursorUp creates additional cursor, at the same X (if possible), one Y less.
|
||||
func (h *BufPane) SpawnMultiCursorUp() bool {
|
||||
if h.Cursor.Y == 0 {
|
||||
return false
|
||||
}
|
||||
h.Cursor.GotoLoc(buffer.Loc{h.Cursor.X, h.Cursor.Y - 1})
|
||||
h.Cursor.Relocate()
|
||||
// SpawnMultiCursorUpN is not an action
|
||||
func (h *BufPane) SpawnMultiCursorUpN(n int) bool {
|
||||
lastC := h.Buf.GetCursor(h.Buf.NumCursors() - 1)
|
||||
var c *buffer.Cursor
|
||||
if !h.Buf.Settings["softwrap"].(bool) {
|
||||
if n > 0 && lastC.Y == 0 {
|
||||
return false
|
||||
}
|
||||
if n < 0 && lastC.Y+1 == h.Buf.LinesNum() {
|
||||
return false
|
||||
}
|
||||
|
||||
h.Buf.DeselectCursors()
|
||||
|
||||
c = buffer.NewCursor(h.Buf, buffer.Loc{lastC.X, lastC.Y - n})
|
||||
c.LastVisualX = lastC.LastVisualX
|
||||
c.X = c.GetCharPosInLine(h.Buf.LineBytes(c.Y), c.LastVisualX)
|
||||
c.Relocate()
|
||||
} else {
|
||||
vloc := h.VLocFromLoc(lastC.Loc)
|
||||
sloc := h.Scroll(vloc.SLoc, -n)
|
||||
if sloc == vloc.SLoc {
|
||||
return false
|
||||
}
|
||||
|
||||
h.Buf.DeselectCursors()
|
||||
|
||||
vloc.SLoc = sloc
|
||||
vloc.VisualX = lastC.LastVisualX
|
||||
c = buffer.NewCursor(h.Buf, h.LocFromVLoc(vloc))
|
||||
c.LastVisualX = lastC.LastVisualX
|
||||
}
|
||||
|
||||
c := buffer.NewCursor(h.Buf, buffer.Loc{h.Cursor.X, h.Cursor.Y + 1})
|
||||
h.Buf.AddCursor(c)
|
||||
h.Buf.SetCurCursor(h.Buf.NumCursors() - 1)
|
||||
h.Buf.MergeCursors()
|
||||
|
@ -1777,20 +1839,14 @@ func (h *BufPane) SpawnMultiCursorUp() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// SpawnMultiCursorUp creates additional cursor, at the same X (if possible), one Y less.
|
||||
func (h *BufPane) SpawnMultiCursorUp() bool {
|
||||
return h.SpawnMultiCursorUpN(1)
|
||||
}
|
||||
|
||||
// SpawnMultiCursorDown creates additional cursor, at the same X (if possible), one Y more.
|
||||
func (h *BufPane) SpawnMultiCursorDown() bool {
|
||||
if h.Cursor.Y+1 == h.Buf.LinesNum() {
|
||||
return false
|
||||
}
|
||||
h.Cursor.GotoLoc(buffer.Loc{h.Cursor.X, h.Cursor.Y + 1})
|
||||
h.Cursor.Relocate()
|
||||
|
||||
c := buffer.NewCursor(h.Buf, buffer.Loc{h.Cursor.X, h.Cursor.Y - 1})
|
||||
h.Buf.AddCursor(c)
|
||||
h.Buf.SetCurCursor(h.Buf.NumCursors() - 1)
|
||||
h.Buf.MergeCursors()
|
||||
h.Relocate()
|
||||
return true
|
||||
return h.SpawnMultiCursorUpN(-1)
|
||||
}
|
||||
|
||||
// SpawnMultiCursorSelect adds a cursor at the beginning of each line of a selection
|
||||
|
@ -1827,11 +1883,27 @@ func (h *BufPane) SpawnMultiCursorSelect() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// MouseMultiCursor is a mouse action which puts a new cursor at the mouse position
|
||||
// MouseMultiCursor is a mouse action which puts a new cursor at the mouse position,
|
||||
// or removes a cursor if it is already there
|
||||
func (h *BufPane) MouseMultiCursor(e *tcell.EventMouse) bool {
|
||||
b := h.Buf
|
||||
mx, my := e.Position()
|
||||
// ignore click on the status line
|
||||
if my >= h.BufView().Y+h.BufView().Height {
|
||||
return false
|
||||
}
|
||||
mouseLoc := h.LocFromVisual(buffer.Loc{X: mx, Y: my})
|
||||
|
||||
if h.Buf.NumCursors() > 1 {
|
||||
cursors := h.Buf.GetCursors()
|
||||
for _, c := range cursors {
|
||||
if c.Loc == mouseLoc {
|
||||
h.Buf.RemoveCursor(c.Num)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c := buffer.NewCursor(b, mouseLoc)
|
||||
b.AddCursor(c)
|
||||
b.MergeCursors()
|
||||
|
|
|
@ -201,11 +201,20 @@ modSearch:
|
|||
}, true
|
||||
}
|
||||
|
||||
var mstate MouseState = MousePress
|
||||
if strings.HasSuffix(k, "Drag") {
|
||||
k = k[:len(k)-4]
|
||||
mstate = MouseDrag
|
||||
} else if strings.HasSuffix(k, "Release") {
|
||||
k = k[:len(k)-7]
|
||||
mstate = MouseRelease
|
||||
}
|
||||
// See if we can find the key in bindingMouse
|
||||
if code, ok := mouseEvents[k]; ok {
|
||||
return MouseEvent{
|
||||
btn: code,
|
||||
mod: modifiers,
|
||||
btn: code,
|
||||
mod: modifiers,
|
||||
state: mstate,
|
||||
}, true
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
"github.com/zyedidia/micro/v2/internal/buffer"
|
||||
"github.com/zyedidia/micro/v2/internal/clipboard"
|
||||
"github.com/zyedidia/micro/v2/internal/config"
|
||||
"github.com/zyedidia/micro/v2/internal/display"
|
||||
ulua "github.com/zyedidia/micro/v2/internal/lua"
|
||||
|
@ -17,6 +16,8 @@ import (
|
|||
"github.com/zyedidia/tcell/v2"
|
||||
)
|
||||
|
||||
type BufAction interface{}
|
||||
|
||||
// BufKeyAction represents an action bound to a key.
|
||||
type BufKeyAction func(*BufPane) bool
|
||||
|
||||
|
@ -44,8 +45,9 @@ func init() {
|
|||
BufBindings = NewKeyTree()
|
||||
}
|
||||
|
||||
// LuaAction makes a BufKeyAction from a lua function.
|
||||
func LuaAction(fn string) func(*BufPane) bool {
|
||||
// LuaAction makes an action from a lua function. It returns either a BufKeyAction
|
||||
// or a BufMouseAction depending on the event type.
|
||||
func LuaAction(fn string, k Event) BufAction {
|
||||
luaFn := strings.Split(fn, ".")
|
||||
if len(luaFn) <= 1 {
|
||||
return nil
|
||||
|
@ -55,33 +57,42 @@ func LuaAction(fn string) func(*BufPane) bool {
|
|||
if pl == nil {
|
||||
return nil
|
||||
}
|
||||
return func(h *BufPane) bool {
|
||||
val, err := pl.Call(plFn, luar.New(ulua.L, h))
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
if v, ok := val.(lua.LBool); !ok {
|
||||
return false
|
||||
} else {
|
||||
return bool(v)
|
||||
}
|
||||
|
||||
var action BufAction
|
||||
switch k.(type) {
|
||||
case KeyEvent, KeySequenceEvent, RawEvent:
|
||||
action = BufKeyAction(func(h *BufPane) bool {
|
||||
val, err := pl.Call(plFn, luar.New(ulua.L, h))
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
if v, ok := val.(lua.LBool); !ok {
|
||||
return false
|
||||
} else {
|
||||
return bool(v)
|
||||
}
|
||||
})
|
||||
case MouseEvent:
|
||||
action = BufMouseAction(func(h *BufPane, te *tcell.EventMouse) bool {
|
||||
val, err := pl.Call(plFn, luar.New(ulua.L, h), luar.New(ulua.L, te))
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
if v, ok := val.(lua.LBool); !ok {
|
||||
return false
|
||||
} else {
|
||||
return bool(v)
|
||||
}
|
||||
})
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
// BufMapKey maps an event to an action
|
||||
// BufMapEvent maps an event to an action
|
||||
func BufMapEvent(k Event, action string) {
|
||||
config.Bindings["buffer"][k.Name()] = action
|
||||
|
||||
switch e := k.(type) {
|
||||
case KeyEvent, KeySequenceEvent, RawEvent:
|
||||
bufMapKey(e, action)
|
||||
case MouseEvent:
|
||||
bufMapMouse(e, action)
|
||||
}
|
||||
}
|
||||
|
||||
func bufMapKey(k Event, action string) {
|
||||
var actionfns []func(*BufPane) bool
|
||||
var actionfns []BufAction
|
||||
var names []string
|
||||
var types []byte
|
||||
for i := 0; ; i++ {
|
||||
|
@ -102,7 +113,7 @@ func bufMapKey(k Event, action string) {
|
|||
action = ""
|
||||
}
|
||||
|
||||
var afn func(*BufPane) bool
|
||||
var afn BufAction
|
||||
if strings.HasPrefix(a, "command:") {
|
||||
a = strings.SplitN(a, ":", 2)[1]
|
||||
afn = CommandAction(a)
|
||||
|
@ -113,7 +124,7 @@ func bufMapKey(k Event, action string) {
|
|||
names = append(names, "")
|
||||
} else if strings.HasPrefix(a, "lua:") {
|
||||
a = strings.SplitN(a, ":", 2)[1]
|
||||
afn = LuaAction(a)
|
||||
afn = LuaAction(a, k)
|
||||
if afn == nil {
|
||||
screen.TermMessage("Lua Error:", a, "does not exist")
|
||||
continue
|
||||
|
@ -129,13 +140,16 @@ func bufMapKey(k Event, action string) {
|
|||
} else if f, ok := BufKeyActions[a]; ok {
|
||||
afn = f
|
||||
names = append(names, a)
|
||||
} else if f, ok := BufMouseActions[a]; ok {
|
||||
afn = f
|
||||
names = append(names, a)
|
||||
} else {
|
||||
screen.TermMessage("Error in bindings: action", a, "does not exist")
|
||||
continue
|
||||
}
|
||||
actionfns = append(actionfns, afn)
|
||||
}
|
||||
bufAction := func(h *BufPane) bool {
|
||||
bufAction := func(h *BufPane, te *tcell.EventMouse) bool {
|
||||
cursors := h.Buf.GetCursors()
|
||||
success := true
|
||||
for i, a := range actionfns {
|
||||
|
@ -147,7 +161,7 @@ func bufMapKey(k Event, action string) {
|
|||
h.Buf.SetCurCursor(c.Num)
|
||||
h.Cursor = c
|
||||
if i == 0 || (success && types[i-1] == '&') || (!success && types[i-1] == '|') || (types[i-1] == ',') {
|
||||
innerSuccess = innerSuccess && h.execAction(a, names[i], j)
|
||||
innerSuccess = innerSuccess && h.execAction(a, names[i], j, te)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
@ -159,17 +173,13 @@ func bufMapKey(k Event, action string) {
|
|||
return true
|
||||
}
|
||||
|
||||
BufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(bufAction))
|
||||
}
|
||||
|
||||
// BufMapMouse maps a mouse event to an action
|
||||
func bufMapMouse(k MouseEvent, action string) {
|
||||
if f, ok := BufMouseActions[action]; ok {
|
||||
BufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f))
|
||||
} else {
|
||||
// TODO
|
||||
// delete(BufMouseBindings, k)
|
||||
bufMapKey(k, action)
|
||||
switch e := k.(type) {
|
||||
case KeyEvent, KeySequenceEvent, RawEvent:
|
||||
BufBindings.RegisterKeyBinding(e, BufKeyActionGeneral(func(h *BufPane) bool {
|
||||
return bufAction(h, nil)
|
||||
}))
|
||||
case MouseEvent:
|
||||
BufBindings.RegisterMouseBinding(e, BufMouseActionGeneral(bufAction))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,11 +210,15 @@ type BufPane struct {
|
|||
// Cursor is the currently active buffer cursor
|
||||
Cursor *buffer.Cursor
|
||||
|
||||
// Since tcell doesn't differentiate between a mouse release event
|
||||
// and a mouse move event with no keys pressed, we need to keep
|
||||
// track of whether or not the mouse was pressed (or not released) last event to determine
|
||||
// mouse release events
|
||||
mouseReleased bool
|
||||
// Since tcell doesn't differentiate between a mouse press event
|
||||
// and a mouse move event with button pressed (nor between a mouse
|
||||
// release event and a mouse move event with no buttons pressed),
|
||||
// we need to keep track of whether or not the mouse was previously
|
||||
// pressed, to determine mouse release and mouse drag events.
|
||||
// Moreover, since in case of a release event tcell doesn't tell us
|
||||
// which button was released, we need to keep track of which
|
||||
// (possibly multiple) buttons were pressed previously.
|
||||
mousePressed map[MouseEvent]bool
|
||||
|
||||
// We need to keep track of insert key press toggle
|
||||
isOverwriteMode bool
|
||||
|
@ -250,7 +264,7 @@ func newBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane {
|
|||
h.tab = tab
|
||||
|
||||
h.Cursor = h.Buf.GetActiveCursor()
|
||||
h.mouseReleased = true
|
||||
h.mousePressed = make(map[MouseEvent]bool)
|
||||
|
||||
return h
|
||||
}
|
||||
|
@ -322,6 +336,12 @@ func (h *BufPane) PluginCBRune(cb string, r rune) bool {
|
|||
return b
|
||||
}
|
||||
|
||||
func (h *BufPane) resetMouse() {
|
||||
for me := range h.mousePressed {
|
||||
delete(h.mousePressed, me)
|
||||
}
|
||||
}
|
||||
|
||||
// OpenBuffer opens the given buffer in this pane.
|
||||
func (h *BufPane) OpenBuffer(b *buffer.Buffer) {
|
||||
h.Buf.Close()
|
||||
|
@ -332,7 +352,7 @@ func (h *BufPane) OpenBuffer(b *buffer.Buffer) {
|
|||
h.initialRelocate()
|
||||
// Set mouseReleased to true because we assume the mouse is not being
|
||||
// pressed when the editor is opened
|
||||
h.mouseReleased = true
|
||||
h.resetMouse()
|
||||
// Set isOverwriteMode to false, because we assume we are in the default
|
||||
// mode when editor is opened
|
||||
h.isOverwriteMode = false
|
||||
|
@ -446,50 +466,32 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
|
|||
h.DoRuneInsert(e.Rune())
|
||||
}
|
||||
case *tcell.EventMouse:
|
||||
cancel := false
|
||||
switch e.Buttons() {
|
||||
case tcell.Button1:
|
||||
_, my := e.Position()
|
||||
if h.Buf.Type.Kind != buffer.BTInfo.Kind && h.Buf.Settings["statusline"].(bool) && my >= h.GetView().Y+h.GetView().Height-1 {
|
||||
cancel = true
|
||||
}
|
||||
case tcell.ButtonNone:
|
||||
// Mouse event with no click
|
||||
if !h.mouseReleased {
|
||||
// Mouse was just released
|
||||
|
||||
// mx, my := e.Position()
|
||||
// mouseLoc := h.LocFromVisual(buffer.Loc{X: mx, Y: my})
|
||||
|
||||
// we could finish the selection based on the release location as described
|
||||
// below but when the mouse click is within the scroll margin this will
|
||||
// cause a scroll and selection even for a simple mouse click which is
|
||||
// not good
|
||||
// for terminals that don't support mouse motion events, selection via
|
||||
// the mouse won't work but this is ok
|
||||
|
||||
// Relocating here isn't really necessary because the cursor will
|
||||
// be in the right place from the last mouse event
|
||||
// However, if we are running in a terminal that doesn't support mouse motion
|
||||
// events, this still allows the user to make selections, except only after they
|
||||
// release the mouse
|
||||
|
||||
// if !h.doubleClick && !h.tripleClick {
|
||||
// h.Cursor.SetSelectionEnd(h.Cursor.Loc)
|
||||
// }
|
||||
if h.Cursor.HasSelection() {
|
||||
h.Cursor.CopySelection(clipboard.PrimaryReg)
|
||||
}
|
||||
h.mouseReleased = true
|
||||
}
|
||||
}
|
||||
|
||||
if !cancel {
|
||||
if e.Buttons() != tcell.ButtonNone {
|
||||
me := MouseEvent{
|
||||
btn: e.Buttons(),
|
||||
mod: metaToAlt(e.Modifiers()),
|
||||
btn: e.Buttons(),
|
||||
mod: metaToAlt(e.Modifiers()),
|
||||
state: MousePress,
|
||||
}
|
||||
isDrag := len(h.mousePressed) > 0
|
||||
|
||||
if e.Buttons() & ^(tcell.WheelUp|tcell.WheelDown|tcell.WheelLeft|tcell.WheelRight) != tcell.ButtonNone {
|
||||
h.mousePressed[me] = true
|
||||
}
|
||||
|
||||
if isDrag {
|
||||
me.state = MouseDrag
|
||||
}
|
||||
h.DoMouseEvent(me, e)
|
||||
} else {
|
||||
// Mouse event with no click - mouse was just released.
|
||||
// If there were multiple mouse buttons pressed, we don't know which one
|
||||
// was actually released, so we assume they all were released.
|
||||
for me := range h.mousePressed {
|
||||
delete(h.mousePressed, me)
|
||||
|
||||
me.state = MouseRelease
|
||||
h.DoMouseEvent(me, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
h.Buf.MergeCursors()
|
||||
|
@ -509,6 +511,14 @@ func (h *BufPane) HandleEvent(event tcell.Event) {
|
|||
InfoBar.ClearGutter()
|
||||
}
|
||||
}
|
||||
|
||||
cursors := h.Buf.GetCursors()
|
||||
for _, c := range cursors {
|
||||
if c.NewTrailingWsY != c.Y && (!c.HasSelection() ||
|
||||
(c.NewTrailingWsY != c.CurSelection[0].Y && c.NewTrailingWsY != c.CurSelection[1].Y)) {
|
||||
c.NewTrailingWsY = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bindings returns the current bindings tree for this buffer.
|
||||
|
@ -534,7 +544,7 @@ func (h *BufPane) DoKeyEvent(e Event) bool {
|
|||
return more
|
||||
}
|
||||
|
||||
func (h *BufPane) execAction(action func(*BufPane) bool, name string, cursor int) bool {
|
||||
func (h *BufPane) execAction(action BufAction, name string, cursor int, te *tcell.EventMouse) bool {
|
||||
if name != "Autocomplete" && name != "CycleAutocompleteBack" {
|
||||
h.Buf.HasSuggestions = false
|
||||
}
|
||||
|
@ -542,7 +552,13 @@ func (h *BufPane) execAction(action func(*BufPane) bool, name string, cursor int
|
|||
_, isMulti := MultiActions[name]
|
||||
if (!isMulti && cursor == 0) || isMulti {
|
||||
if h.PluginCB("pre" + name) {
|
||||
success := action(h)
|
||||
var success bool
|
||||
switch a := action.(type) {
|
||||
case BufKeyAction:
|
||||
success = a(h)
|
||||
case BufMouseAction:
|
||||
success = a(h, te)
|
||||
}
|
||||
success = success && h.PluginCB("on"+name)
|
||||
|
||||
if isMulti {
|
||||
|
@ -804,6 +820,8 @@ var BufKeyActions = map[string]BufKeyAction{
|
|||
// BufMouseActions contains the list of all possible mouse actions the bufhandler could execute
|
||||
var BufMouseActions = map[string]BufMouseAction{
|
||||
"MousePress": (*BufPane).MousePress,
|
||||
"MouseDrag": (*BufPane).MouseDrag,
|
||||
"MouseRelease": (*BufPane).MouseRelease,
|
||||
"MouseMultiCursor": (*BufPane).MouseMultiCursor,
|
||||
}
|
||||
|
||||
|
|
|
@ -375,7 +375,7 @@ func (h *BufPane) ReopenCmd(args []string) {
|
|||
|
||||
func (h *BufPane) openHelp(page string) error {
|
||||
if data, err := config.FindRuntimeFile(config.RTHelp, page).Data(); err != nil {
|
||||
return errors.New(fmt.Sprint("Unable to load help text", page, "\n", err))
|
||||
return errors.New(fmt.Sprintf("Unable to load help text for %s: %v", page, err))
|
||||
} else {
|
||||
helpBuffer := buffer.NewBufferFromString(string(data), page+".md", buffer.BTHelp)
|
||||
helpBuffer.SetName("Help " + page)
|
||||
|
@ -532,8 +532,12 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error {
|
|||
}
|
||||
}
|
||||
|
||||
for _, b := range buffer.OpenBuffers {
|
||||
b.SetOptionNative(option, nativeValue)
|
||||
if local {
|
||||
MainTab().CurPane().Buf.SetOptionNative(option, nativeValue)
|
||||
} else {
|
||||
for _, b := range buffer.OpenBuffers {
|
||||
b.SetOptionNative(option, nativeValue)
|
||||
}
|
||||
}
|
||||
|
||||
return config.WriteSettings(filepath.Join(config.ConfigDir, "settings.json"))
|
||||
|
@ -697,6 +701,34 @@ func (h *BufPane) QuitCmd(args []string) {
|
|||
h.Quit()
|
||||
}
|
||||
|
||||
func convertLine(h *BufPane, line string) (int, error) {
|
||||
lineNum := 0
|
||||
var err error
|
||||
|
||||
// Check for special negative movement beginning from the end of the file
|
||||
if strings.HasPrefix(line, "~") {
|
||||
lineNum, err = strconv.Atoi(line[1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
lineNum = h.Buf.LinesNum() + 1 - lineNum
|
||||
} else {
|
||||
lineNum, err = strconv.Atoi(line)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Check for relative numbers
|
||||
if strings.HasPrefix(line, "-") || strings.HasPrefix(line, "+") {
|
||||
lineNum = h.Buf.GetActiveCursor().Y + 1 + lineNum
|
||||
}
|
||||
}
|
||||
|
||||
lineNum = util.Clamp(lineNum-1, 0, h.Buf.LinesNum()-1)
|
||||
|
||||
return lineNum, err
|
||||
}
|
||||
|
||||
// GotoCmd is a command that will send the cursor to a certain
|
||||
// position in the buffer
|
||||
// For example: `goto line`, or `goto line:col`
|
||||
|
@ -704,37 +736,30 @@ func (h *BufPane) GotoCmd(args []string) {
|
|||
if len(args) <= 0 {
|
||||
InfoBar.Error("Not enough arguments")
|
||||
} else {
|
||||
line, col := 0, 0
|
||||
var err error
|
||||
h.RemoveAllMultiCursors()
|
||||
if strings.Contains(args[0], ":") {
|
||||
parts := strings.SplitN(args[0], ":", 2)
|
||||
line, err := strconv.Atoi(parts[0])
|
||||
line, err = convertLine(h, parts[0])
|
||||
if err != nil {
|
||||
InfoBar.Error(err)
|
||||
return
|
||||
}
|
||||
col, err := strconv.Atoi(parts[1])
|
||||
col, err = strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
InfoBar.Error(err)
|
||||
return
|
||||
}
|
||||
if line < 0 {
|
||||
line = h.Buf.LinesNum() + 1 + line
|
||||
}
|
||||
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
|
||||
col = util.Clamp(col-1, 0, util.CharacterCount(h.Buf.LineBytes(line)))
|
||||
h.GotoLoc(buffer.Loc{col, line})
|
||||
} else {
|
||||
line, err := strconv.Atoi(args[0])
|
||||
line, err = convertLine(h, args[0])
|
||||
if err != nil {
|
||||
InfoBar.Error(err)
|
||||
return
|
||||
}
|
||||
if line < 0 {
|
||||
line = h.Buf.LinesNum() + 1 + line
|
||||
}
|
||||
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
|
||||
h.GotoLoc(buffer.Loc{0, line})
|
||||
}
|
||||
h.GotoLoc(buffer.Loc{col, line})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,11 +92,13 @@ var bufdefaults = map[string]string{
|
|||
"Esc": "Escape,Deselect,ClearInfo,RemoveAllMultiCursors,UnhighlightSearch",
|
||||
|
||||
// Mouse bindings
|
||||
"MouseWheelUp": "ScrollUp",
|
||||
"MouseWheelDown": "ScrollDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseMiddle": "PastePrimary",
|
||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||
"MouseWheelUp": "ScrollUp",
|
||||
"MouseWheelDown": "ScrollDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseLeftDrag": "MouseDrag",
|
||||
"MouseLeftRelease": "MouseRelease",
|
||||
"MouseMiddle": "PastePrimary",
|
||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||
|
||||
"Alt-n": "SpawnMultiCursor",
|
||||
"AltShiftUp": "SpawnMultiCursorUp",
|
||||
|
@ -175,8 +177,10 @@ var infodefaults = map[string]string{
|
|||
"Esc": "AbortCommand",
|
||||
|
||||
// Mouse bindings
|
||||
"MouseWheelUp": "HistoryUp",
|
||||
"MouseWheelDown": "HistoryDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseMiddle": "PastePrimary",
|
||||
"MouseWheelUp": "HistoryUp",
|
||||
"MouseWheelDown": "HistoryDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseLeftDrag": "MouseDrag",
|
||||
"MouseLeftRelease": "MouseRelease",
|
||||
"MouseMiddle": "PastePrimary",
|
||||
}
|
||||
|
|
|
@ -95,11 +95,13 @@ var bufdefaults = map[string]string{
|
|||
"Esc": "Escape,Deselect,ClearInfo,RemoveAllMultiCursors,UnhighlightSearch",
|
||||
|
||||
// Mouse bindings
|
||||
"MouseWheelUp": "ScrollUp",
|
||||
"MouseWheelDown": "ScrollDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseMiddle": "PastePrimary",
|
||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||
"MouseWheelUp": "ScrollUp",
|
||||
"MouseWheelDown": "ScrollDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseLeftDrag": "MouseDrag",
|
||||
"MouseLeftRelease": "MouseRelease",
|
||||
"MouseMiddle": "PastePrimary",
|
||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||
|
||||
"Alt-n": "SpawnMultiCursor",
|
||||
"Alt-m": "SpawnMultiCursorSelect",
|
||||
|
@ -178,8 +180,10 @@ var infodefaults = map[string]string{
|
|||
"Esc": "AbortCommand",
|
||||
|
||||
// Mouse bindings
|
||||
"MouseWheelUp": "HistoryUp",
|
||||
"MouseWheelDown": "HistoryDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseMiddle": "PastePrimary",
|
||||
"MouseWheelUp": "HistoryUp",
|
||||
"MouseWheelDown": "HistoryDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseLeftDrag": "MouseDrag",
|
||||
"MouseLeftRelease": "MouseRelease",
|
||||
"MouseMiddle": "PastePrimary",
|
||||
}
|
||||
|
|
|
@ -100,11 +100,20 @@ func (k KeySequenceEvent) Name() string {
|
|||
return buf.String()
|
||||
}
|
||||
|
||||
type MouseState int
|
||||
|
||||
const (
|
||||
MousePress = iota
|
||||
MouseDrag
|
||||
MouseRelease
|
||||
)
|
||||
|
||||
// MouseEvent is a mouse event with a mouse button and
|
||||
// any possible key modifiers
|
||||
type MouseEvent struct {
|
||||
btn tcell.ButtonMask
|
||||
mod tcell.ModMask
|
||||
btn tcell.ButtonMask
|
||||
mod tcell.ModMask
|
||||
state MouseState
|
||||
}
|
||||
|
||||
func (m MouseEvent) Name() string {
|
||||
|
@ -122,9 +131,17 @@ func (m MouseEvent) Name() string {
|
|||
mod = "Ctrl-"
|
||||
}
|
||||
|
||||
state := ""
|
||||
switch m.state {
|
||||
case MouseDrag:
|
||||
state = "Drag"
|
||||
case MouseRelease:
|
||||
state = "Release"
|
||||
}
|
||||
|
||||
for k, v := range mouseEvents {
|
||||
if v == m.btn {
|
||||
return fmt.Sprintf("%s%s", mod, k)
|
||||
return fmt.Sprintf("%s%s%s", mod, k, state)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
// CommandComplete autocompletes commands
|
||||
func CommandComplete(b *buffer.Buffer) ([]string, []string) {
|
||||
c := b.GetActiveCursor()
|
||||
input, argstart := buffer.GetArg(b)
|
||||
input, argstart := b.GetArg()
|
||||
|
||||
var suggestions []string
|
||||
for cmd := range commands {
|
||||
|
@ -38,7 +38,7 @@ func CommandComplete(b *buffer.Buffer) ([]string, []string) {
|
|||
// HelpComplete autocompletes help topics
|
||||
func HelpComplete(b *buffer.Buffer) ([]string, []string) {
|
||||
c := b.GetActiveCursor()
|
||||
input, argstart := buffer.GetArg(b)
|
||||
input, argstart := b.GetArg()
|
||||
|
||||
var suggestions []string
|
||||
|
||||
|
@ -77,6 +77,24 @@ func colorschemeComplete(input string) (string, []string) {
|
|||
return chosen, suggestions
|
||||
}
|
||||
|
||||
// filetypeComplete autocompletes filetype
|
||||
func filetypeComplete(input string) (string, []string) {
|
||||
var suggestions []string
|
||||
|
||||
for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
|
||||
if strings.HasPrefix(f.Name(), input) {
|
||||
suggestions = append(suggestions, f.Name())
|
||||
}
|
||||
}
|
||||
|
||||
var chosen string
|
||||
if len(suggestions) == 1 {
|
||||
chosen = suggestions[0]
|
||||
}
|
||||
|
||||
return chosen, suggestions
|
||||
}
|
||||
|
||||
func contains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
|
@ -89,7 +107,7 @@ func contains(s []string, e string) bool {
|
|||
// OptionComplete autocompletes options
|
||||
func OptionComplete(b *buffer.Buffer) ([]string, []string) {
|
||||
c := b.GetActiveCursor()
|
||||
input, argstart := buffer.GetArg(b)
|
||||
input, argstart := b.GetArg()
|
||||
|
||||
var suggestions []string
|
||||
for option := range config.GlobalSettings {
|
||||
|
@ -116,7 +134,7 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
|
|||
c := b.GetActiveCursor()
|
||||
l := b.LineBytes(c.Y)
|
||||
l = util.SliceStart(l, c.X)
|
||||
input, argstart := buffer.GetArg(b)
|
||||
input, argstart := b.GetArg()
|
||||
|
||||
completeValue := false
|
||||
args := bytes.Split(l, []byte{' '})
|
||||
|
@ -172,6 +190,8 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
|
|||
switch inputOpt {
|
||||
case "colorscheme":
|
||||
_, suggestions = colorschemeComplete(input)
|
||||
case "filetype":
|
||||
_, suggestions = filetypeComplete(input)
|
||||
case "fileformat":
|
||||
if strings.HasPrefix("unix", input) {
|
||||
suggestions = append(suggestions, "unix")
|
||||
|
@ -196,6 +216,13 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
|
|||
if strings.HasPrefix("terminal", input) {
|
||||
suggestions = append(suggestions, "terminal")
|
||||
}
|
||||
case "matchbracestyle":
|
||||
if strings.HasPrefix("underline", input) {
|
||||
suggestions = append(suggestions, "underline")
|
||||
}
|
||||
if strings.HasPrefix("highlight", input) {
|
||||
suggestions = append(suggestions, "highlight")
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(suggestions)
|
||||
|
@ -210,7 +237,7 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
|
|||
// PluginCmdComplete autocompletes the plugin command
|
||||
func PluginCmdComplete(b *buffer.Buffer) ([]string, []string) {
|
||||
c := b.GetActiveCursor()
|
||||
input, argstart := buffer.GetArg(b)
|
||||
input, argstart := b.GetArg()
|
||||
|
||||
var suggestions []string
|
||||
for _, cmd := range PluginCmds {
|
||||
|
@ -232,7 +259,7 @@ func PluginComplete(b *buffer.Buffer) ([]string, []string) {
|
|||
c := b.GetActiveCursor()
|
||||
l := b.LineBytes(c.Y)
|
||||
l = util.SliceStart(l, c.X)
|
||||
input, argstart := buffer.GetArg(b)
|
||||
input, argstart := b.GetArg()
|
||||
|
||||
completeValue := false
|
||||
args := bytes.Split(l, []byte{' '})
|
||||
|
|
|
@ -164,6 +164,21 @@ func InitTabs(bufs []*buffer.Buffer) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.RestartCallback = func() {
|
||||
// The mouse could be released after the screen was stopped, so that
|
||||
// we couldn't catch the mouse release event and would erroneously think
|
||||
// that it is still pressed. So need to reset the mouse release state
|
||||
// after the screen is restarted.
|
||||
for _, t := range Tabs.List {
|
||||
t.release = true
|
||||
for _, p := range t.Panes {
|
||||
if bp, ok := p.(*BufPane); ok {
|
||||
bp.resetMouse()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func MainTab() *Tab {
|
||||
|
@ -214,34 +229,40 @@ func NewTabFromPane(x, y, width, height int, pane Pane) *Tab {
|
|||
// HandleEvent takes a tcell event and usually dispatches it to the current
|
||||
// active pane. However if the event is a resize or a mouse event where the user
|
||||
// is interacting with the UI (resizing splits) then the event is consumed here
|
||||
// If the event is a mouse event in a pane, that pane will become active and get
|
||||
// the event
|
||||
// If the event is a mouse press event in a pane, that pane will become active
|
||||
// and get the event
|
||||
func (t *Tab) HandleEvent(event tcell.Event) {
|
||||
switch e := event.(type) {
|
||||
case *tcell.EventMouse:
|
||||
mx, my := e.Position()
|
||||
switch e.Buttons() {
|
||||
case tcell.Button1:
|
||||
btn := e.Buttons()
|
||||
switch {
|
||||
case btn & ^(tcell.WheelUp|tcell.WheelDown|tcell.WheelLeft|tcell.WheelRight) != tcell.ButtonNone:
|
||||
// button press or drag
|
||||
wasReleased := t.release
|
||||
t.release = false
|
||||
if t.resizing != nil {
|
||||
var size int
|
||||
if t.resizing.Kind == views.STVert {
|
||||
size = mx - t.resizing.X
|
||||
} else {
|
||||
size = my - t.resizing.Y + 1
|
||||
|
||||
if btn == tcell.Button1 {
|
||||
if t.resizing != nil {
|
||||
var size int
|
||||
if t.resizing.Kind == views.STVert {
|
||||
size = mx - t.resizing.X
|
||||
} else {
|
||||
size = my - t.resizing.Y + 1
|
||||
}
|
||||
t.resizing.ResizeSplit(size)
|
||||
t.Resize()
|
||||
return
|
||||
}
|
||||
if wasReleased {
|
||||
t.resizing = t.GetMouseSplitNode(buffer.Loc{mx, my})
|
||||
if t.resizing != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.resizing.ResizeSplit(size)
|
||||
t.Resize()
|
||||
return
|
||||
}
|
||||
|
||||
if wasReleased {
|
||||
t.resizing = t.GetMouseSplitNode(buffer.Loc{mx, my})
|
||||
if t.resizing != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i, p := range t.Panes {
|
||||
v := p.GetView()
|
||||
inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
|
||||
|
@ -251,10 +272,15 @@ func (t *Tab) HandleEvent(event tcell.Event) {
|
|||
}
|
||||
}
|
||||
}
|
||||
case tcell.ButtonNone:
|
||||
t.resizing = nil
|
||||
case btn == tcell.ButtonNone:
|
||||
// button release
|
||||
t.release = true
|
||||
if t.resizing != nil {
|
||||
t.resizing = nil
|
||||
return
|
||||
}
|
||||
default:
|
||||
// wheel move
|
||||
for _, p := range t.Panes {
|
||||
v := p.GetView()
|
||||
inpane := mx >= v.X && mx < v.X+v.Width && my >= v.Y && my < v.Y+v.Height
|
||||
|
|
|
@ -64,7 +64,7 @@ func (b *Buffer) CycleAutocomplete(forward bool) {
|
|||
|
||||
// GetWord gets the most recent word separated by any separator
|
||||
// (whitespace, punctuation, any non alphanumeric character)
|
||||
func GetWord(b *Buffer) ([]byte, int) {
|
||||
func (b *Buffer) GetWord() ([]byte, int) {
|
||||
c := b.GetActiveCursor()
|
||||
l := b.LineBytes(c.Y)
|
||||
l = util.SliceStart(l, c.X)
|
||||
|
@ -83,7 +83,7 @@ func GetWord(b *Buffer) ([]byte, int) {
|
|||
}
|
||||
|
||||
// GetArg gets the most recent word (separated by ' ' only)
|
||||
func GetArg(b *Buffer) (string, int) {
|
||||
func (b *Buffer) GetArg() (string, int) {
|
||||
c := b.GetActiveCursor()
|
||||
l := b.LineBytes(c.Y)
|
||||
l = util.SliceStart(l, c.X)
|
||||
|
@ -104,7 +104,7 @@ func GetArg(b *Buffer) (string, int) {
|
|||
// FileComplete autocompletes filenames
|
||||
func FileComplete(b *Buffer) ([]string, []string) {
|
||||
c := b.GetActiveCursor()
|
||||
input, argstart := GetArg(b)
|
||||
input, argstart := b.GetArg()
|
||||
|
||||
sep := string(os.PathSeparator)
|
||||
dirs := strings.Split(input, sep)
|
||||
|
@ -153,7 +153,7 @@ func FileComplete(b *Buffer) ([]string, []string) {
|
|||
// BufferComplete autocompletes based on previous words in the buffer
|
||||
func BufferComplete(b *Buffer) ([]string, []string) {
|
||||
c := b.GetActiveCursor()
|
||||
input, argstart := GetWord(b)
|
||||
input, argstart := b.GetWord()
|
||||
|
||||
if argstart == -1 {
|
||||
return []string{}, []string{}
|
||||
|
|
|
@ -568,6 +568,13 @@ func (b *Buffer) RelocateCursors() {
|
|||
}
|
||||
}
|
||||
|
||||
// DeselectCursors removes selection from all cursors
|
||||
func (b *Buffer) DeselectCursors() {
|
||||
for _, c := range b.cursors {
|
||||
c.Deselect(true)
|
||||
}
|
||||
}
|
||||
|
||||
// RuneAt returns the rune at a given location in the buffer
|
||||
func (b *Buffer) RuneAt(loc Loc) rune {
|
||||
line := b.LineBytes(loc.Y)
|
||||
|
@ -1056,7 +1063,7 @@ func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool, boo
|
|||
leftChar = curLine[start.X-1]
|
||||
}
|
||||
var i int
|
||||
if startChar == braceType[0] || leftChar == braceType[0] {
|
||||
if startChar == braceType[0] || (leftChar == braceType[0] && startChar != braceType[1]) {
|
||||
for y := start.Y; y < b.LinesNum(); y++ {
|
||||
l := []rune(string(b.LineBytes(y)))
|
||||
xInit := 0
|
||||
|
@ -1087,24 +1094,24 @@ func (b *Buffer) FindMatchingBrace(braceType [2]rune, start Loc) (Loc, bool, boo
|
|||
l := []rune(string(b.lines[y].data))
|
||||
xInit := len(l) - 1
|
||||
if y == start.Y {
|
||||
if leftChar == braceType[1] {
|
||||
xInit = start.X - 1
|
||||
} else {
|
||||
if startChar == braceType[1] {
|
||||
xInit = start.X
|
||||
} else {
|
||||
xInit = start.X - 1
|
||||
}
|
||||
}
|
||||
for x := xInit; x >= 0; x-- {
|
||||
r := l[x]
|
||||
if r == braceType[0] {
|
||||
if r == braceType[1] {
|
||||
i++
|
||||
} else if r == braceType[0] {
|
||||
i--
|
||||
if i == 0 {
|
||||
if leftChar == braceType[1] {
|
||||
return Loc{x, y}, true, true
|
||||
if startChar == braceType[1] {
|
||||
return Loc{x, y}, false, true
|
||||
}
|
||||
return Loc{x, y}, false, true
|
||||
return Loc{x, y}, true, true
|
||||
}
|
||||
} else if r == braceType[1] {
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,11 @@ type Cursor struct {
|
|||
// to know what the original selection was
|
||||
OrigSelection [2]Loc
|
||||
|
||||
// The line number where a new trailing whitespace has been added
|
||||
// or -1 if there is no new trailing whitespace at this cursor.
|
||||
// This is used for checking if a trailing whitespace should be highlighted
|
||||
NewTrailingWsY int
|
||||
|
||||
// Which cursor index is this (for multiple cursors)
|
||||
Num int
|
||||
}
|
||||
|
@ -38,6 +43,8 @@ func NewCursor(b *Buffer, l Loc) *Cursor {
|
|||
c := &Cursor{
|
||||
buf: b,
|
||||
Loc: l,
|
||||
|
||||
NewTrailingWsY: -1,
|
||||
}
|
||||
c.StoreVisualX()
|
||||
return c
|
||||
|
|
|
@ -106,6 +106,10 @@ func (eh *EventHandler) DoTextEvent(t *TextEvent, useUndo bool) {
|
|||
c.Relocate()
|
||||
c.LastVisualX = c.GetVisualX()
|
||||
}
|
||||
|
||||
if useUndo {
|
||||
eh.updateTrailingWs(t)
|
||||
}
|
||||
}
|
||||
|
||||
// ExecuteTextEvent runs a text event
|
||||
|
@ -290,6 +294,7 @@ func (eh *EventHandler) UndoOneEvent() {
|
|||
if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
|
||||
t.C = *eh.cursors[teCursor.Num]
|
||||
eh.cursors[teCursor.Num].Goto(teCursor)
|
||||
eh.cursors[teCursor.Num].NewTrailingWsY = teCursor.NewTrailingWsY
|
||||
} else {
|
||||
teCursor.Num = -1
|
||||
}
|
||||
|
@ -333,6 +338,7 @@ func (eh *EventHandler) RedoOneEvent() {
|
|||
if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
|
||||
t.C = *eh.cursors[teCursor.Num]
|
||||
eh.cursors[teCursor.Num].Goto(teCursor)
|
||||
eh.cursors[teCursor.Num].NewTrailingWsY = teCursor.NewTrailingWsY
|
||||
} else {
|
||||
teCursor.Num = -1
|
||||
}
|
||||
|
@ -342,3 +348,58 @@ func (eh *EventHandler) RedoOneEvent() {
|
|||
|
||||
eh.UndoStack.Push(t)
|
||||
}
|
||||
|
||||
// updateTrailingWs updates the cursor's trailing whitespace status after a text event
|
||||
func (eh *EventHandler) updateTrailingWs(t *TextEvent) {
|
||||
if len(t.Deltas) != 1 {
|
||||
return
|
||||
}
|
||||
text := t.Deltas[0].Text
|
||||
start := t.Deltas[0].Start
|
||||
end := t.Deltas[0].End
|
||||
|
||||
c := eh.cursors[eh.active]
|
||||
isEol := func(loc Loc) bool {
|
||||
return loc.X == util.CharacterCount(eh.buf.LineBytes(loc.Y))
|
||||
}
|
||||
if t.EventType == TextEventInsert && c.Loc == end && isEol(end) {
|
||||
var addedTrailingWs bool
|
||||
addedAfterWs := false
|
||||
addedWsOnly := false
|
||||
if start.Y == end.Y {
|
||||
addedTrailingWs = util.HasTrailingWhitespace(text)
|
||||
addedWsOnly = util.IsBytesWhitespace(text)
|
||||
addedAfterWs = start.X > 0 && util.IsWhitespace(c.buf.RuneAt(Loc{start.X - 1, start.Y}))
|
||||
} else {
|
||||
lastnl := bytes.LastIndex(text, []byte{'\n'})
|
||||
addedTrailingWs = util.HasTrailingWhitespace(text[lastnl+1:])
|
||||
}
|
||||
|
||||
if addedTrailingWs && !(addedAfterWs && addedWsOnly) {
|
||||
c.NewTrailingWsY = c.Y
|
||||
} else if !addedTrailingWs {
|
||||
c.NewTrailingWsY = -1
|
||||
}
|
||||
} else if t.EventType == TextEventRemove && c.Loc == start && isEol(start) {
|
||||
removedAfterWs := util.HasTrailingWhitespace(eh.buf.LineBytes(start.Y))
|
||||
var removedWsOnly bool
|
||||
if start.Y == end.Y {
|
||||
removedWsOnly = util.IsBytesWhitespace(text)
|
||||
} else {
|
||||
firstnl := bytes.Index(text, []byte{'\n'})
|
||||
removedWsOnly = util.IsBytesWhitespace(text[:firstnl])
|
||||
}
|
||||
|
||||
if removedAfterWs && !removedWsOnly {
|
||||
c.NewTrailingWsY = c.Y
|
||||
} else if !removedAfterWs {
|
||||
c.NewTrailingWsY = -1
|
||||
}
|
||||
} else if c.NewTrailingWsY != -1 && start.Y != end.Y && c.Loc.GreaterThan(start) &&
|
||||
((t.EventType == TextEventInsert && c.Y == c.NewTrailingWsY+(end.Y-start.Y)) ||
|
||||
(t.EventType == TextEventRemove && c.Y == c.NewTrailingWsY-(end.Y-start.Y))) {
|
||||
// The cursor still has its new trailingws
|
||||
// but its line number was shifted by insert or remove of lines above
|
||||
c.NewTrailingWsY = c.Y
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ func overwriteFile(name string, enc encoding.Encoding, fn func(io.Writer) error,
|
|||
screen.TempStart(screenb)
|
||||
return err
|
||||
}
|
||||
} else if writeCloser, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
|
||||
} else if writeCloser, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -129,9 +129,17 @@ func AddRuntimeFilesFromAssets(fileType RTFiletype, directory, pattern string) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
assetLoop:
|
||||
for _, f := range files {
|
||||
if ok, _ := path.Match(pattern, f); ok {
|
||||
AddRuntimeFile(fileType, assetFile(path.Join(directory, f)))
|
||||
af := assetFile(path.Join(directory, f))
|
||||
for _, rf := range realFiles[fileType] {
|
||||
if af.Name() == rf.Name() {
|
||||
continue assetLoop
|
||||
}
|
||||
}
|
||||
AddRuntimeFile(fileType, af)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +226,15 @@ func InitRuntimeFiles() {
|
|||
|
||||
plugdir = filepath.Join("runtime", "plugins")
|
||||
if files, err := rt.AssetDir(plugdir); err == nil {
|
||||
outer:
|
||||
for _, d := range files {
|
||||
for _, p := range Plugins {
|
||||
if p.Name == d {
|
||||
log.Println(p.Name, "built-in plugin overridden by user-defined one")
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
|
||||
if srcs, err := rt.AssetDir(filepath.Join(plugdir, d)); err == nil {
|
||||
p := new(Plugin)
|
||||
p.Name = d
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -42,18 +43,19 @@ func init() {
|
|||
|
||||
// Options with validators
|
||||
var optionValidators = map[string]optionValidator{
|
||||
"autosave": validateNonNegativeValue,
|
||||
"clipboard": validateClipboard,
|
||||
"detectlimit": validateNonNegativeValue,
|
||||
"tabsize": validatePositiveValue,
|
||||
"scrollmargin": validateNonNegativeValue,
|
||||
"scrollspeed": validateNonNegativeValue,
|
||||
"colorscheme": validateColorscheme,
|
||||
"colorcolumn": validateNonNegativeValue,
|
||||
"fileformat": validateLineEnding,
|
||||
"encoding": validateEncoding,
|
||||
"multiopen": validateMultiOpen,
|
||||
"reload": validateReload,
|
||||
"autosave": validateNonNegativeValue,
|
||||
"clipboard": validateClipboard,
|
||||
"detectlimit": validateNonNegativeValue,
|
||||
"tabsize": validatePositiveValue,
|
||||
"scrollmargin": validateNonNegativeValue,
|
||||
"scrollspeed": validateNonNegativeValue,
|
||||
"colorscheme": validateColorscheme,
|
||||
"colorcolumn": validateNonNegativeValue,
|
||||
"fileformat": validateLineEnding,
|
||||
"encoding": validateEncoding,
|
||||
"multiopen": validateMultiOpen,
|
||||
"reload": validateReload,
|
||||
"matchbracestyle": validateMatchBraceStyle,
|
||||
}
|
||||
|
||||
func ReadSettings() error {
|
||||
|
@ -274,51 +276,61 @@ func GetGlobalOption(name string) interface{} {
|
|||
}
|
||||
|
||||
var defaultCommonSettings = map[string]interface{}{
|
||||
"autoindent": true,
|
||||
"autosu": false,
|
||||
"backup": true,
|
||||
"backupdir": "",
|
||||
"basename": false,
|
||||
"colorcolumn": float64(0),
|
||||
"cursorline": true,
|
||||
"detectlimit": float64(100),
|
||||
"diffgutter": false,
|
||||
"encoding": "utf-8",
|
||||
"eofnewline": true,
|
||||
"fastdirty": false,
|
||||
"fileformat": "unix",
|
||||
"filetype": "unknown",
|
||||
"hlsearch": false,
|
||||
"incsearch": true,
|
||||
"ignorecase": true,
|
||||
"indentchar": " ",
|
||||
"keepautoindent": false,
|
||||
"matchbrace": true,
|
||||
"mkparents": false,
|
||||
"permbackup": false,
|
||||
"readonly": false,
|
||||
"reload": "prompt",
|
||||
"rmtrailingws": false,
|
||||
"ruler": true,
|
||||
"relativeruler": false,
|
||||
"savecursor": false,
|
||||
"saveundo": false,
|
||||
"scrollbar": false,
|
||||
"scrollmargin": float64(3),
|
||||
"scrollspeed": float64(2),
|
||||
"smartpaste": true,
|
||||
"softwrap": false,
|
||||
"splitbottom": true,
|
||||
"splitright": true,
|
||||
"statusformatl": "$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
|
||||
"statusformatr": "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help",
|
||||
"statusline": true,
|
||||
"syntax": true,
|
||||
"tabmovement": false,
|
||||
"tabsize": float64(4),
|
||||
"tabstospaces": false,
|
||||
"useprimary": true,
|
||||
"wordwrap": false,
|
||||
"autoindent": true,
|
||||
"autosu": false,
|
||||
"backup": true,
|
||||
"backupdir": "",
|
||||
"basename": false,
|
||||
"colorcolumn": float64(0),
|
||||
"cursorline": true,
|
||||
"detectlimit": float64(100),
|
||||
"diffgutter": false,
|
||||
"encoding": "utf-8",
|
||||
"eofnewline": true,
|
||||
"fastdirty": false,
|
||||
"fileformat": defaultFileFormat(),
|
||||
"filetype": "unknown",
|
||||
"hlsearch": false,
|
||||
"hltaberrors": false,
|
||||
"hltrailingws": false,
|
||||
"incsearch": true,
|
||||
"ignorecase": true,
|
||||
"indentchar": " ",
|
||||
"keepautoindent": false,
|
||||
"matchbrace": true,
|
||||
"matchbracestyle": "underline",
|
||||
"mkparents": false,
|
||||
"permbackup": false,
|
||||
"readonly": false,
|
||||
"reload": "prompt",
|
||||
"rmtrailingws": false,
|
||||
"ruler": true,
|
||||
"relativeruler": false,
|
||||
"savecursor": false,
|
||||
"saveundo": false,
|
||||
"scrollbar": false,
|
||||
"scrollmargin": float64(3),
|
||||
"scrollspeed": float64(2),
|
||||
"smartpaste": true,
|
||||
"softwrap": false,
|
||||
"splitbottom": true,
|
||||
"splitright": true,
|
||||
"statusformatl": "$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
|
||||
"statusformatr": "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help",
|
||||
"statusline": true,
|
||||
"syntax": true,
|
||||
"tabmovement": false,
|
||||
"tabsize": float64(4),
|
||||
"tabstospaces": false,
|
||||
"useprimary": true,
|
||||
"wordwrap": false,
|
||||
}
|
||||
|
||||
func defaultFileFormat() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return "dos"
|
||||
}
|
||||
return "unix"
|
||||
}
|
||||
|
||||
func GetInfoBarOffset() int {
|
||||
|
@ -542,6 +554,22 @@ func validateReload(option string, value interface{}) error {
|
|||
case "prompt", "auto", "disabled":
|
||||
default:
|
||||
return errors.New(option + " must be 'prompt', 'auto' or 'disabled'")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateMatchBraceStyle(option string, value interface{}) error {
|
||||
val, ok := value.(string)
|
||||
|
||||
if !ok {
|
||||
errors.New("Expected string type for matchbracestyle")
|
||||
}
|
||||
|
||||
switch val {
|
||||
case "underline", "highlight":
|
||||
default:
|
||||
return errors.New(option + " must be 'underline' or 'highlight'")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -129,6 +129,11 @@ func (w *BufWindow) updateDisplayInfo() {
|
|||
w.bufHeight--
|
||||
}
|
||||
|
||||
scrollbarWidth := 0
|
||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height && w.Width > 0 {
|
||||
scrollbarWidth = 1
|
||||
}
|
||||
|
||||
w.hasMessage = len(b.Messages) > 0
|
||||
|
||||
// We need to know the string length of the largest line number
|
||||
|
@ -146,13 +151,13 @@ func (w *BufWindow) updateDisplayInfo() {
|
|||
w.gutterOffset += w.maxLineNumLength + 1
|
||||
}
|
||||
|
||||
prevBufWidth := w.bufWidth
|
||||
|
||||
w.bufWidth = w.Width - w.gutterOffset
|
||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
||||
w.bufWidth--
|
||||
if w.gutterOffset > w.Width-scrollbarWidth {
|
||||
w.gutterOffset = w.Width - scrollbarWidth
|
||||
}
|
||||
|
||||
prevBufWidth := w.bufWidth
|
||||
w.bufWidth = w.Width - w.gutterOffset - scrollbarWidth
|
||||
|
||||
if w.bufWidth != prevBufWidth && w.Buf.Settings["softwrap"].(bool) {
|
||||
for _, c := range w.Buf.GetCursors() {
|
||||
c.LastVisualX = c.GetVisualX()
|
||||
|
@ -277,13 +282,17 @@ func (w *BufWindow) drawGutter(vloc *buffer.Loc, bloc *buffer.Loc) {
|
|||
break
|
||||
}
|
||||
}
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
|
||||
vloc.X++
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
|
||||
vloc.X++
|
||||
for i := 0; i < 2 && vloc.X < w.gutterOffset; i++ {
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
|
||||
vloc.X++
|
||||
}
|
||||
}
|
||||
|
||||
func (w *BufWindow) drawDiffGutter(backgroundStyle tcell.Style, softwrapped bool, vloc *buffer.Loc, bloc *buffer.Loc) {
|
||||
if vloc.X >= w.gutterOffset {
|
||||
return
|
||||
}
|
||||
|
||||
symbol := ' '
|
||||
styleName := ""
|
||||
|
||||
|
@ -319,26 +328,28 @@ func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, vloc
|
|||
} else {
|
||||
lineInt = bloc.Y - cursorLine
|
||||
}
|
||||
lineNum := strconv.Itoa(util.Abs(lineInt))
|
||||
lineNum := []rune(strconv.Itoa(util.Abs(lineInt)))
|
||||
|
||||
// Write the spaces before the line number if necessary
|
||||
for i := 0; i < w.maxLineNumLength-len(lineNum); i++ {
|
||||
for i := 0; i < w.maxLineNumLength-len(lineNum) && vloc.X < w.gutterOffset; i++ {
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
||||
vloc.X++
|
||||
}
|
||||
// Write the actual line number
|
||||
for _, ch := range lineNum {
|
||||
if softwrapped {
|
||||
for i := 0; i < len(lineNum) && vloc.X < w.gutterOffset; i++ {
|
||||
if softwrapped || (w.bufWidth == 0 && w.Buf.Settings["softwrap"] == true) {
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
||||
} else {
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ch, nil, lineNumStyle)
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, lineNum[i], nil, lineNumStyle)
|
||||
}
|
||||
vloc.X++
|
||||
}
|
||||
|
||||
// Write the extra space
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
||||
vloc.X++
|
||||
if vloc.X < w.gutterOffset {
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
||||
vloc.X++
|
||||
}
|
||||
}
|
||||
|
||||
// getStyle returns the highlight style for the given character position
|
||||
|
@ -407,7 +418,9 @@ func (w *BufWindow) displayBuffer() {
|
|||
if found {
|
||||
matchingBraces = append(matchingBraces, mb)
|
||||
if !left {
|
||||
matchingBraces = append(matchingBraces, curLoc)
|
||||
if b.Settings["matchbracestyle"].(string) != "highlight" {
|
||||
matchingBraces = append(matchingBraces, curLoc)
|
||||
}
|
||||
} else {
|
||||
matchingBraces = append(matchingBraces, curLoc.Move(-1, b))
|
||||
}
|
||||
|
@ -482,6 +495,12 @@ func (w *BufWindow) displayBuffer() {
|
|||
vloc.X = w.gutterOffset
|
||||
}
|
||||
|
||||
bline := b.LineBytes(bloc.Y)
|
||||
blineLen := util.CharacterCount(bline)
|
||||
|
||||
leadingwsEnd := len(util.GetLeadingWhitespace(bline))
|
||||
trailingwsStart := blineLen - util.CharacterCount(util.GetTrailingWhitespace(bline))
|
||||
|
||||
line, nColsBeforeStart, bslice, startStyle := w.getStartInfo(w.StartCol, bloc.Y)
|
||||
if startStyle != nil {
|
||||
curStyle = *startStyle
|
||||
|
@ -505,6 +524,37 @@ func (w *BufWindow) displayBuffer() {
|
|||
// over cursor-line and color-column
|
||||
dontOverrideBackground := origBg != defBg
|
||||
|
||||
if b.Settings["hltaberrors"].(bool) {
|
||||
if s, ok := config.Colorscheme["tab-error"]; ok {
|
||||
isTab := (r == '\t') || (r == ' ' && !showcursor)
|
||||
if (b.Settings["tabstospaces"].(bool) && isTab) ||
|
||||
(!b.Settings["tabstospaces"].(bool) && bloc.X < leadingwsEnd && r == ' ' && !isTab) {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Background(fg)
|
||||
dontOverrideBackground = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if b.Settings["hltrailingws"].(bool) {
|
||||
if s, ok := config.Colorscheme["trailingws"]; ok {
|
||||
if bloc.X >= trailingwsStart && bloc.X < blineLen {
|
||||
hl := true
|
||||
for _, c := range cursors {
|
||||
if c.NewTrailingWsY == bloc.Y {
|
||||
hl = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if hl {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Background(fg)
|
||||
dontOverrideBackground = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range cursors {
|
||||
if c.HasSelection() &&
|
||||
(bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) ||
|
||||
|
@ -557,7 +607,15 @@ func (w *BufWindow) displayBuffer() {
|
|||
|
||||
for _, mb := range matchingBraces {
|
||||
if mb.X == bloc.X && mb.Y == bloc.Y {
|
||||
style = style.Underline(true)
|
||||
if b.Settings["matchbracestyle"].(string) == "highlight" {
|
||||
if s, ok := config.Colorscheme["match-brace"]; ok {
|
||||
style = s
|
||||
} else {
|
||||
style = style.Reverse(true)
|
||||
}
|
||||
} else {
|
||||
style = style.Underline(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -609,7 +667,7 @@ func (w *BufWindow) displayBuffer() {
|
|||
wordwidth := 0
|
||||
|
||||
totalwidth := w.StartCol - nColsBeforeStart
|
||||
for len(line) > 0 {
|
||||
for len(line) > 0 && vloc.X < maxWidth {
|
||||
r, combc, size := util.DecodeCharacter(line)
|
||||
line = line[size:]
|
||||
|
||||
|
|
|
@ -110,6 +110,8 @@ func (w *TermWindow) Display() {
|
|||
}
|
||||
if w.State.CursorVisible() && w.active {
|
||||
curx, cury := w.State.Cursor()
|
||||
screen.ShowCursor(curx+w.X, cury+w.Y)
|
||||
if curx < w.Width && cury < w.Height {
|
||||
screen.ShowCursor(curx+w.X, cury+w.Y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,10 @@ var Screen tcell.Screen
|
|||
// Events is the channel of tcell events
|
||||
var Events chan (tcell.Event)
|
||||
|
||||
// RestartCallback is called when the screen is restarted after it was
|
||||
// temporarily shut down
|
||||
var RestartCallback func()
|
||||
|
||||
// The lock is necessary since the screen is polled on a separate thread
|
||||
var lock sync.Mutex
|
||||
|
||||
|
@ -134,6 +138,10 @@ func TempStart(screenWasNil bool) {
|
|||
if !screenWasNil {
|
||||
Init()
|
||||
Unlock()
|
||||
|
||||
if RestartCallback != nil {
|
||||
RestartCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/blang/semver"
|
||||
runewidth "github.com/mattn/go-runewidth"
|
||||
|
@ -315,7 +316,7 @@ func ReplaceHome(path string) (string, error) {
|
|||
// This is used for opening files like util.go:10:5 to specify a line and column
|
||||
// Special cases like Windows Absolute path (C:\myfile.txt:10:5) are handled correctly.
|
||||
func GetPathAndCursorPosition(path string) (string, []string) {
|
||||
re := regexp.MustCompile(`([\s\S]+?)(?::(\d+))(?::(\d+))?`)
|
||||
re := regexp.MustCompile(`([\s\S]+?)(?::(\d+))(?::(\d+))?$`)
|
||||
match := re.FindStringSubmatch(path)
|
||||
// no lines/columns were specified in the path, return just the path with no cursor location
|
||||
if len(match) == 0 {
|
||||
|
@ -363,6 +364,28 @@ func GetLeadingWhitespace(b []byte) []byte {
|
|||
return ws
|
||||
}
|
||||
|
||||
// GetTrailingWhitespace returns the trailing whitespace of the given byte array
|
||||
func GetTrailingWhitespace(b []byte) []byte {
|
||||
ws := []byte{}
|
||||
for len(b) > 0 {
|
||||
r, size := utf8.DecodeLastRune(b)
|
||||
if IsWhitespace(r) {
|
||||
ws = append([]byte(string(r)), ws...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
||||
b = b[:len(b)-size]
|
||||
}
|
||||
return ws
|
||||
}
|
||||
|
||||
// HasTrailingWhitespace returns true if the given byte array ends with a whitespace
|
||||
func HasTrailingWhitespace(b []byte) bool {
|
||||
r, _ := utf8.DecodeLastRune(b)
|
||||
return IsWhitespace(r)
|
||||
}
|
||||
|
||||
// IntOpt turns a float64 setting to an int
|
||||
func IntOpt(opt interface{}) int {
|
||||
return int(opt.(float64))
|
||||
|
|
|
@ -185,6 +185,9 @@ func (n *Node) hResizeSplit(i int, size int) bool {
|
|||
|
||||
// ResizeSplit resizes a certain split to a given size
|
||||
func (n *Node) ResizeSplit(size int) bool {
|
||||
if size <= 0 {
|
||||
return false
|
||||
}
|
||||
if len(n.parent.children) <= 1 {
|
||||
// cannot resize a lone node
|
||||
return false
|
||||
|
|
|
@ -28,3 +28,6 @@ color-link color-column "#2D2F31"
|
|||
#No extended types (bool in C, etc.)
|
||||
#color-link type.extended "default"
|
||||
#Plain brackets
|
||||
color-link match-brace "#1D1F21,#62B1FE"
|
||||
color-link tab-error "#D75F5F"
|
||||
color-link trailingws "#D75F5F"
|
||||
|
|
|
@ -26,3 +26,6 @@ color-link color-column "254"
|
|||
#No extended types (bool in C, &c.) and plain brackets
|
||||
color-link type.extended "241,231"
|
||||
color-link symbol.brackets "241,231"
|
||||
color-link match-brace "231,28"
|
||||
color-link tab-error "210"
|
||||
color-link trailingws "210"
|
||||
|
|
|
@ -42,3 +42,6 @@ color-link gutter-warning "red"
|
|||
color-link color-column "cyan"
|
||||
color-link underlined.url "underline blue, white"
|
||||
color-link divider "blue"
|
||||
color-link match-brace "black,cyan"
|
||||
color-link tab-error "brightred"
|
||||
color-link trailingws "brightred"
|
||||
|
|
|
@ -38,3 +38,6 @@ color-link color-column "#f26522"
|
|||
color-link constant.bool "bold #55ffff"
|
||||
color-link constant.bool.true "bold #85ff85"
|
||||
color-link constant.bool.false "bold #ff8585"
|
||||
color-link match-brace "#1e2124,#55ffff"
|
||||
color-link tab-error "#d75f5f"
|
||||
color-link trailingws "#d75f5f"
|
||||
|
|
|
@ -29,3 +29,6 @@ color-link color-column "#2C2C2C"
|
|||
color-link type.extended "default"
|
||||
#color-link symbol.brackets "default"
|
||||
color-link symbol.tag "#AE81FF,#242424"
|
||||
color-link match-brace "#242424,#7A9EC2"
|
||||
color-link tab-error "#D75F5F"
|
||||
color-link trailingws "#D75F5F"
|
||||
|
|
|
@ -29,3 +29,6 @@ color-link color-column "#323232"
|
|||
color-link type.extended "default"
|
||||
#color-link symbol.brackets "default"
|
||||
color-link symbol.tag "#AE81FF,#282828"
|
||||
color-link match-brace "#282828,#AE81FF"
|
||||
color-link tab-error "#D75F5F"
|
||||
color-link trailingws "#D75F5F"
|
||||
|
|
|
@ -43,3 +43,7 @@ color-link cursor-line "#44475A,#F8F8F2"
|
|||
color-link color-column "#44475A"
|
||||
color-link type.extended "default"
|
||||
|
||||
color-link match-brace "#282A36,#FF79C6"
|
||||
|
||||
color-link tab-error "#D75F5F"
|
||||
color-link trailingws "#D75F5F"
|
||||
|
|
|
@ -33,3 +33,6 @@ color-link type "bold #3cc83c,#001e28"
|
|||
color-link type.keyword "bold #5aaae6,#001e28"
|
||||
color-link type.extended "#ffffff,#001e28"
|
||||
color-link underlined "#608b4e,#001e28"
|
||||
color-link match-brace "#001e28,#5aaae6"
|
||||
color-link tab-error "#d75f5f"
|
||||
color-link trailingws "#d75f5f"
|
||||
|
|
|
@ -33,3 +33,6 @@ color-link type "bold #004080,#f0f0f0"
|
|||
color-link type.keyword "bold #780050,#f0f0f0"
|
||||
color-link type.extended "#000000,#f0f0f0"
|
||||
color-link underlined "#3f7f5f,#f0f0f0"
|
||||
color-link match-brace "#f0f0f0,#780050"
|
||||
color-link tab-error "#ff8787"
|
||||
color-link trailingws "#ff8787"
|
||||
|
|
|
@ -33,3 +33,6 @@ color-link type "bold #3cc83c,#2d0023"
|
|||
color-link type.keyword "bold #5aaae6,#2d0023"
|
||||
color-link type.extended "#ffffff,#2d0023"
|
||||
color-link underlined "#886484,#2d0023"
|
||||
color-link match-brace "#2d0023,#5aaae6"
|
||||
color-link tab-error "#d75f5f"
|
||||
color-link trailingws "#d75f5f"
|
||||
|
|
|
@ -24,3 +24,6 @@ color-link diff-modified "yellow"
|
|||
color-link diff-deleted "red"
|
||||
color-link gutter-error ",red"
|
||||
color-link gutter-warning "red"
|
||||
color-link match-brace "black,cyan"
|
||||
color-link tab-error "brightred"
|
||||
color-link trailingws "brightred"
|
||||
|
|
|
@ -24,3 +24,6 @@ color-link gutter-warning "#EDB443,#11151C"
|
|||
color-link cursor-line "#091F2E"
|
||||
color-link color-column "#11151C"
|
||||
color-link symbol "#99D1CE,#0C1014"
|
||||
color-link match-brace "#0C1014,#D26937"
|
||||
color-link tab-error "#D75F5F"
|
||||
color-link trailingws "#D75F5F"
|
||||
|
|
|
@ -24,3 +24,6 @@ color-link cursor-line "#3c3836"
|
|||
color-link color-column "#79740e"
|
||||
color-link statusline "#ebdbb2,#665c54"
|
||||
color-link tabbar "#ebdbb2,#665c54"
|
||||
color-link match-brace "#282828,#d3869b"
|
||||
color-link tab-error "#d75f5f"
|
||||
color-link trailingws "#d75f5f"
|
||||
|
|
|
@ -21,3 +21,6 @@ color-link cursor-line "237"
|
|||
color-link color-column "237"
|
||||
color-link statusline "223,237"
|
||||
color-link tabbar "223,237"
|
||||
color-link match-brace "235,72"
|
||||
color-link tab-error "167"
|
||||
color-link trailingws "167"
|
||||
|
|
|
@ -30,3 +30,6 @@ color-link tabbar "#80DEEA,#3b4d56"
|
|||
color-link todo "bold #C792EA,#263238"
|
||||
color-link type "#FFCB6B,#263238"
|
||||
color-link underlined "underline #EEFFFF,#263238"
|
||||
color-link match-brace "#263238,#C792EA"
|
||||
color-link tab-error "#D75F5F"
|
||||
color-link trailingws "#D75F5F"
|
||||
|
|
|
@ -23,3 +23,6 @@ color-link gutter-error "#CB4B16"
|
|||
color-link gutter-warning "#E6DB74"
|
||||
color-link cursor-line "#323232"
|
||||
color-link color-column "#323232"
|
||||
color-link match-brace "#1D0000,#AE81FF"
|
||||
color-link tab-error "#D75F5F"
|
||||
color-link trailingws "#D75F5F"
|
||||
|
|
|
@ -29,3 +29,6 @@ color-link color-column "#323232"
|
|||
color-link type.extended "default"
|
||||
#color-link symbol.brackets "default"
|
||||
color-link symbol.tag "#AE81FF,#282828"
|
||||
color-link match-brace "#282828,#AE81FF"
|
||||
color-link tab-error "#D75F5F"
|
||||
color-link trailingws "#D75F5F"
|
||||
|
|
|
@ -34,3 +34,6 @@ color-link todo "#8B98AB"
|
|||
color-link type "#66D9EF"
|
||||
color-link type.keyword "#C678DD"
|
||||
color-link underlined "#8996A8"
|
||||
color-link match-brace "#21252C,#C678DD"
|
||||
color-link tab-error "#D75F5F"
|
||||
color-link trailingws "#D75F5F"
|
||||
|
|
|
@ -28,6 +28,10 @@ color-link tabbar "bold #b1b1b1,#232323"
|
|||
color-link cursor-line "#353535"
|
||||
color-link color-column "#353535"
|
||||
color-link space "underline #e6e1dc,#2b2b2b"
|
||||
color-link tab-error "#d75f5f"
|
||||
color-link trailingws "#d75f5f"
|
||||
|
||||
#the Python syntax definition are wrong. This is not how you should do decorators!
|
||||
color-link brightgreen "#edb753,#2b2b2b"
|
||||
|
||||
color-link match-brace "#2b2b2b,#a5c261"
|
||||
|
|
|
@ -10,6 +10,7 @@ color-link ignore "default"
|
|||
color-link error ",brightred"
|
||||
color-link todo ",brightyellow"
|
||||
color-link hlsearch "black,yellow"
|
||||
color-link statusline "black,white"
|
||||
color-link indent-char "black"
|
||||
color-link line-number "yellow"
|
||||
color-link current-line-number "red"
|
||||
|
@ -27,3 +28,6 @@ color-link type.extended "default"
|
|||
color-link symbol.brackets "default"
|
||||
#Color shebangs the comment color
|
||||
color-link preproc.shebang "comment"
|
||||
color-link match-brace ",magenta"
|
||||
color-link tab-error "brightred"
|
||||
color-link trailingws "brightred"
|
||||
|
|
|
@ -26,3 +26,6 @@ color-link cursor-line "#003541"
|
|||
color-link color-column "#003541"
|
||||
color-link type.extended "#839496,#002833"
|
||||
color-link symbol.brackets "#839496,#002833"
|
||||
color-link match-brace "#002833,#268BD2"
|
||||
color-link tab-error "#D75F5F"
|
||||
color-link trailingws "#D75F5F"
|
||||
|
|
|
@ -25,3 +25,6 @@ color-link cursor-line "black"
|
|||
color-link color-column "black"
|
||||
color-link type.extended "default"
|
||||
color-link symbol.brackets "default"
|
||||
color-link match-brace ",blue"
|
||||
color-link tab-error "brightred"
|
||||
color-link trailingws "brightred"
|
||||
|
|
|
@ -24,3 +24,6 @@ color-link gutter-warning "88"
|
|||
color-link cursor-line "229"
|
||||
#color-link color-column "196"
|
||||
color-link current-line-number "246"
|
||||
color-line match-brace "230,22"
|
||||
color-link tab-error "210"
|
||||
color-link trailingws "210"
|
||||
|
|
|
@ -35,3 +35,6 @@ color-link todo "#8B98AB"
|
|||
color-link type "#F9EE98"
|
||||
color-link type.keyword "#CDA869"
|
||||
color-link underlined "#8996A8"
|
||||
color-link match-brace "#141414,#E0C589"
|
||||
color-link tab-error "#D75F5F"
|
||||
color-link trailingws "#D75F5F"
|
||||
|
|
|
@ -25,3 +25,6 @@ color-link gutter-warning "174,237"
|
|||
color-link cursor-line "238"
|
||||
color-link color-column "238"
|
||||
color-link current-line-number "188,237"
|
||||
color-link match-brace "237,223"
|
||||
color-link tab-error "167"
|
||||
color-link trailingws "167"
|
||||
|
|
|
@ -194,6 +194,7 @@ Here is a list of the colorscheme groups that you can use:
|
|||
* divider (Color of the divider between vertical splits)
|
||||
* message (Color of messages in the bottom line of the screen)
|
||||
* error-message (Color of error messages in the bottom line of the screen)
|
||||
* match-brace (Color of matching brackets when `matchbracestyle` is set to `highlight`)
|
||||
|
||||
Colorschemes must be placed in the `~/.config/micro/colorschemes` directory to
|
||||
be used.
|
||||
|
|
|
@ -31,9 +31,11 @@ quotes here but these are not necessary when entering the command in micro.
|
|||
|
||||
* `quit`: quits micro.
|
||||
|
||||
* `goto 'line'`: jumps to the given line number. A negative number can be
|
||||
passed to jump inward from the end of the file; for example, -5 jumps
|
||||
to the 5th-last line in the file.
|
||||
* `goto 'line[:col]'`: Jumps to the given line (and optional column) number.
|
||||
`line` can be prefixed with `+`, `-` or `~`.
|
||||
+/- will perform relative jumps from the current position of the cursor
|
||||
(e.g. +5 will jump 5 lines down), while ~ will perform a jump inward
|
||||
from the end of the file (e.g. ~5 jumps to the 5th-last line in the file).
|
||||
|
||||
* `replace 'search' 'value' 'flags'?`: This will replace `search` with `value`.
|
||||
The `flags` are optional. Possible flags are:
|
||||
|
|
|
@ -409,8 +409,14 @@ mouse actions)
|
|||
|
||||
```
|
||||
MouseLeft
|
||||
MouseLeftDrag
|
||||
MouseLeftRelease
|
||||
MouseMiddle
|
||||
MouseMiddleDrag
|
||||
MouseMiddleRelease
|
||||
MouseRight
|
||||
MouseRightDrag
|
||||
MouseRightRelease
|
||||
MouseWheelUp
|
||||
MouseWheelDown
|
||||
MouseWheelLeft
|
||||
|
@ -524,11 +530,13 @@ conventions for text editing defaults.
|
|||
"Esc": "Escape",
|
||||
|
||||
// Mouse bindings
|
||||
"MouseWheelUp": "ScrollUp",
|
||||
"MouseWheelDown": "ScrollDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseMiddle": "PastePrimary",
|
||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||
"MouseWheelUp": "ScrollUp",
|
||||
"MouseWheelDown": "ScrollDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseLeftDrag": "MouseDrag",
|
||||
"MouseLeftRelease": "MouseRelease",
|
||||
"MouseMiddle": "PastePrimary",
|
||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||
|
||||
// Multi-cursor bindings
|
||||
"Alt-n": "SpawnMultiCursor",
|
||||
|
@ -634,10 +642,12 @@ are given below:
|
|||
"Esc": "AbortCommand",
|
||||
|
||||
// Mouse bindings
|
||||
"MouseWheelUp": "HistoryUp",
|
||||
"MouseWheelDown": "HistoryDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseMiddle": "PastePrimary"
|
||||
"MouseWheelUp": "HistoryUp",
|
||||
"MouseWheelDown": "HistoryDown",
|
||||
"MouseLeft": "MousePress",
|
||||
"MouseLeftDrag": "MouseDrag",
|
||||
"MouseLeftRelease": "MouseRelease",
|
||||
"MouseMiddle": "PastePrimary"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -164,7 +164,7 @@ Here are the available options:
|
|||
an effect if the file is empty/newly created, because otherwise the fileformat
|
||||
will be automatically detected from the existing line endings.
|
||||
|
||||
default value: `unix`
|
||||
default value: `unix` on Unix systems, `dos` on Windows
|
||||
|
||||
* `filetype`: sets the filetype for the current buffer. Set this option to
|
||||
`off` to completely disable filetype detection.
|
||||
|
@ -181,6 +181,19 @@ Here are the available options:
|
|||
|
||||
default value: `false`
|
||||
|
||||
* `hltaberrors`: highlight tabs when spaces are expected, and spaces when tabs
|
||||
are expected. More precisely: if `tabstospaces` option is on, highlight
|
||||
all tab characters; if `tabstospaces` is off, highlight space characters
|
||||
in the initial indent part of the line.
|
||||
|
||||
default value: `false`
|
||||
|
||||
* `hltrailingws`: highlight trailing whitespaces at ends of lines. Note that
|
||||
it doesn't highlight newly added trailing whitespaces that naturally occur
|
||||
while typing text. It highlights only nasty forgotten trailing whitespaces.
|
||||
|
||||
default value: `false`
|
||||
|
||||
* `incsearch`: enable incremental search in "Find" prompt (matching as you type).
|
||||
|
||||
default value: `true`
|
||||
|
@ -217,11 +230,19 @@ Here are the available options:
|
|||
|
||||
default value: `false`
|
||||
|
||||
* `matchbrace`: underline matching braces for '()', '{}', '[]' when the cursor
|
||||
is on a brace character.
|
||||
* `matchbrace`: show matching braces for '()', '{}', '[]' when the cursor
|
||||
is on a brace character or next to it.
|
||||
|
||||
default value: `true`
|
||||
|
||||
* `matchbracestyle`: whether to underline or highlight matching braces when
|
||||
`matchbrace` is enabled. The color of highlight is determined by the `match-brace`
|
||||
field in the current theme. Possible values:
|
||||
* `underline`: underline matching braces.
|
||||
* `highlight`: use `match-brace` style from the current theme.
|
||||
|
||||
default value: `underline`
|
||||
|
||||
* `mkparents`: if a file is opened on a path that does not exist, the file
|
||||
cannot be saved because the parent directories don't exist. This option lets
|
||||
micro automatically create the parent directories in such a situation.
|
||||
|
@ -502,6 +523,7 @@ so that you can see what the formatting should look like.
|
|||
"linter": true,
|
||||
"literate": true,
|
||||
"matchbrace": true,
|
||||
"matchbracestyle": "underline",
|
||||
"mkparents": false,
|
||||
"mouse": true,
|
||||
"parsecursor": false,
|
||||
|
|
|
@ -67,9 +67,9 @@ which micro defines:
|
|||
by the user. Returns a boolean which defines whether the action should
|
||||
be canceled.
|
||||
|
||||
* `onRune(rune)`: runs when the composed rune has been inserted
|
||||
* `onRune(bufpane, rune)`: runs when the composed rune has been inserted
|
||||
|
||||
* `preRune(rune)`: runs before the composed rune will be inserted
|
||||
* `preRune(bufpane, rune)`: runs before the composed rune will be inserted
|
||||
|
||||
For example a function which is run every time the user saves the buffer
|
||||
would be:
|
||||
|
@ -367,6 +367,7 @@ strings
|
|||
regexp
|
||||
errors
|
||||
time
|
||||
unicode/utf8
|
||||
archive/zip
|
||||
net/http
|
||||
```
|
||||
|
|
|
@ -50,11 +50,11 @@ function preInsertNewline(bp)
|
|||
for i = 1, #autoNewlinePairs do
|
||||
if curRune == charAt(autoNewlinePairs[i], 1) then
|
||||
if nextRune == charAt(autoNewlinePairs[i], 2) then
|
||||
bp:InsertNewline()
|
||||
bp:InsertTab()
|
||||
bp.Buf:Insert(-bp.Cursor.Loc, "\n" .. ws)
|
||||
bp:StartOfLine()
|
||||
bp:CursorLeft()
|
||||
bp:InsertNewline()
|
||||
bp:InsertTab()
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,18 +5,22 @@ detect:
|
|||
|
||||
rules:
|
||||
- identifier: "\\b[A-Z_][0-9A-Z_]+\\b"
|
||||
- type: "\\b(float|double|bool|char|int|short|long|enum|void|struct|union|typedef|(un)?signed|inline)\\b"
|
||||
- type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr)))_t\\b"
|
||||
- type: "\\b(_Atomic|_BitInt|float|double|_Decimal32|_Decimal64|_Decimal128|_Complex|complex|_Imaginary|imaginary|_Bool|bool|char|int|short|long|enum|void|struct|union|typedef|typeof|typeof_unqual|(un)?signed|inline|_Noreturn)\\b"
|
||||
- type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr))|char(8|16|32)|wchar)_t\\b"
|
||||
# GCC float/decimal/fixed types
|
||||
- type: "\\b(_Float16|__fp16|_Float32|_Float32x|_Float64|_Float64x|__float80|_Float128|_Float128x|__float128|__ibm128|__int128|_Fract|_Sat|_Accum)\\b"
|
||||
- type: "\\b[a-z_][0-9a-z_]+(_t|_T)\\b"
|
||||
- statement: "\\b(auto|volatile|register|restrict|static|const|extern)\\b"
|
||||
- statement: "\\b(for|if|while|do|else|case|default|switch)\\b"
|
||||
- statement: "\\b(auto|volatile|register|restrict|_Alignas|alignas|_Alignof|alignof|static|const|constexpr|extern|_Thread_local|thread_local)\\b"
|
||||
- statement: "\\b(for|if|while|do|else|case|default|switch|_Generic|_Static_assert|static_assert)\\b"
|
||||
- statement: "\\b(goto|continue|break|return)\\b"
|
||||
- preproc: "^[[:space:]]*#[[:space:]]*(define|pragma|include|(un|ifn?)def|endif|el(if|se)|if|warning|error)"
|
||||
- statement: "\\b(asm|fortran)\\b"
|
||||
- preproc: "^[[:space:]]*#[[:space:]]*(define|embed|pragma|include|(un|ifn?)def|endif|el(if|ifdef|ifndef|se)|if|line|warning|error|__has_include|__has_embed|__has_c_attribute)"
|
||||
- preproc: "^[[:space:]]*_Pragma\\b"
|
||||
# GCC builtins
|
||||
- statement: "__attribute__[[:space:]]*\\(\\([^)]*\\)\\)"
|
||||
- statement: "__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__"
|
||||
- statement: "__(aligned|asm|builtin|extension|hidden|inline|packed|restrict|section|typeof|weak)__"
|
||||
# Operator Color
|
||||
- symbol.operator: "[-+*/%=<>.:;,~&|^!?]|\\b(sizeof)\\b"
|
||||
- symbol.operator: "[-+*/%=<>.:;,~&|^!?]|\\b(offsetof|sizeof)\\b"
|
||||
- symbol.brackets: "[(){}]|\\[|\\]"
|
||||
# Integer Constants
|
||||
- constant.number: "(\\b([1-9][0-9]*|0[0-7]*|0[Xx][0-9A-Fa-f]+|0[Bb][01]+)([Uu][Ll]?[Ll]?|[Ll][Ll]?[Uu]?)?\\b)"
|
||||
|
|
7
runtime/syntax/cake.yaml
Normal file
7
runtime/syntax/cake.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
filetype: cake
|
||||
detect:
|
||||
filename: "\\.cake$"
|
||||
|
||||
rules:
|
||||
- include: "csharp"
|
||||
- preproc: "^[[:space:]]*#(addin|break|l|load|module|r|reference|tool)"
|
|
@ -10,10 +10,11 @@ rules:
|
|||
# Annotation
|
||||
- identifier.var: "@[A-Za-z]+"
|
||||
|
||||
- preproc: "^[[:space:]]*#[[:space:]]*(define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)"
|
||||
- identifier: "([A-Za-z0-9_]*[[:space:]]*[()])"
|
||||
- type: "\\b(bool|byte|sbyte|char|decimal|double|float|IntPtr|int|uint|long|ulong|object|short|ushort|string|base|this|var|void)\\b"
|
||||
- statement: "\\b(alias|as|case|catch|checked|default|do|dynamic|else|finally|fixed|for|foreach|goto|if|is|lock|new|null|return|switch|throw|try|unchecked|while)\\b"
|
||||
- statement: "\\b(abstract|async|class|const|delegate|enum|event|explicit|extern|get|implicit|in|internal|interface|namespace|operator|out|override|params|partial|private|protected|public|readonly|ref|sealed|set|sizeof|stackalloc|static|struct|typeof|unsafe|using|value|virtual|volatile|yield)\\b"
|
||||
- type: "\\b(bool|byte|sbyte|char|decimal|double|float|IntPtr|int|uint|long|ulong|managed|unmanaged|nint|nuint|object|short|ushort|string|base|this|var|void)\\b"
|
||||
- statement: "\\b(alias|as|case|catch|checked|default|do|dynamic|else|finally|fixed|for|foreach|goto|if|is|lock|new|null|return|switch|throw|try|unchecked|when|while|with)\\b"
|
||||
- statement: "\\b(abstract|add|and|args|async|await|class|const|delegate|enum|event|explicit|extern|file|get|global|implicit|in|init|internal|interface|nameof|namespace|not|notnull|operator|or|out|override|params|partial|private|protected|public|readonly|record|ref|remove|required|scoped|sealed|set|sizeof|stackalloc|static|struct|typeof|unsafe|using|value|virtual|volatile|yield)\\b"
|
||||
# LINQ-only keywords (ones that cannot be used outside of a LINQ query - lots others can)
|
||||
- statement: "\\b(from|where|select|group|info|orderby|join|let|in|on|equals|by|ascending|descending)\\b"
|
||||
- special: "\\b(break|continue)\\b"
|
||||
|
|
31
runtime/syntax/gomod.yaml
Normal file
31
runtime/syntax/gomod.yaml
Normal file
|
@ -0,0 +1,31 @@
|
|||
filetype: gomod
|
||||
|
||||
detect:
|
||||
filename: "go.mod"
|
||||
|
||||
rules:
|
||||
# URL
|
||||
- type: "(^|[ \\t])+\\b([a-zA-Z0-9-]+\\.?)+(/[a-zA-Z0-9-_\\.]+)*\\b"
|
||||
|
||||
# Keywords
|
||||
- special: "(^|[ \\t])+\\b(module|go)\\b"
|
||||
- preproc: "(^|[ \\t])+\\b(toolchain|require|exclude|replace|retract)\\b"
|
||||
- symbol.operator: "=>"
|
||||
|
||||
# Brackets
|
||||
- type: "(\\(|\\))"
|
||||
|
||||
# Go version
|
||||
- type: "(^|[ \\t])+([0-9]+\\.?)+"
|
||||
|
||||
# Version
|
||||
- constant.string: "(^|[ \\t])+v([0-9]+\\.?){3}.*"
|
||||
- constant.number: "(^|[ \\t])+v([0-9]+\\.?){3}"
|
||||
|
||||
- comment:
|
||||
start: "//"
|
||||
end: "$"
|
||||
rules:
|
||||
- todo: "(indirect):?"
|
||||
|
||||
# (^|[ \\t])+ means after start of string or space or tab character
|
|
@ -8,7 +8,7 @@ rules:
|
|||
- preproc: "<!(?i)(DOCTYPE html.*)>"
|
||||
# Opening tag
|
||||
- symbol.tag:
|
||||
start: "<(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h[1-6]|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|s|samp|section|select|small|source|span|strike|strong|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)\\b"
|
||||
start: "<(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h[1-6]|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|main|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|s|samp|section|select|small|source|span|strike|strong|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)\\b"
|
||||
end: ">"
|
||||
rules:
|
||||
- identifier: "\\b(placeholder|style|alt|bgcolor|height|href|id|(aria|data)\\-.+|label|longdesc|name|on(click|focus|load|mouseover)|size|span|src|target|type|value|width|class|charset|content|rel|integrity|crossorigin|for|onsubmit|lang|role)\\b"
|
||||
|
@ -34,7 +34,7 @@ rules:
|
|||
|
||||
# Closing tag
|
||||
- symbol.tag:
|
||||
start: "</(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h[1-6]|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|s|samp|section|select|small|source|span|strike|strong|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)\\b"
|
||||
start: "</(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h[1-6]|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|main|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|s|samp|section|select|small|source|span|strike|strong|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)\\b"
|
||||
end: ">"
|
||||
rules:
|
||||
# Anything in the closing tag is an error
|
||||
|
|
67
runtime/syntax/kvlang.yaml
Normal file
67
runtime/syntax/kvlang.yaml
Normal file
|
@ -0,0 +1,67 @@
|
|||
filetype: "kvlang"
|
||||
|
||||
detect:
|
||||
filename: "\\.kv$"
|
||||
|
||||
rules:
|
||||
# layouts
|
||||
- special: "\\b[a-z].+"
|
||||
- identifier: "\\b(self|app|root)\\b"
|
||||
|
||||
- type: "\\b[A-Z].+"
|
||||
- type: "\\b(AnchorLayout|BoxLayout|FloatLayout|RelativeLayout|GridLayout|PageLayout|StackLayout)\\b"
|
||||
|
||||
- type: "\\b(canvas)\\b"
|
||||
|
||||
# functions
|
||||
- identifier.function: "[a-zA-Z_0-9]+\\("
|
||||
|
||||
# built-in functions
|
||||
- type: "\\b(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes)\\b"
|
||||
- type: "\\b(callable|chr|classmethod|compile|copyright|credits|oct)\\b"
|
||||
- type: "\\b(delattr|dict|dir|display|divmod|enumerate|eval|filter)\\b"
|
||||
- type: "\\b(float|format|frozenset|get_ipython|getattr|globals|type)\\b"
|
||||
- type: "\\b(hash|help|hex|id|input|int|isinstance|issubclass|iter|len)\\b"
|
||||
- type: "\\b(license|list|locals|map|max|memoryview|min|next|object)\\b"
|
||||
- type: "\\b(open|ord|pow|print|property|range|repr|reversed|round|set)\\b"
|
||||
- type: "\\b(setattr|slice|sorted|staticmethod|hasattr|super|tuple|str)\\b"
|
||||
- type: "\\b(vars|zip|exec|sum|complex)\\b"
|
||||
|
||||
# keywords
|
||||
- statement.built_in: "\\b(and|as|assert|async|await|break|class|continue|def)\\b"
|
||||
- statement.built_in: "\\b(del|elif|else|except|finally|for|from|global|if)\\b"
|
||||
- statement.built_in: "\\b(import|in|is|lambda|nonlocal|not|or|pass|raise)\\b"
|
||||
- statement.built_in: "\\b(return|try|while|with|yield|match|case)\\b"
|
||||
|
||||
# operators
|
||||
- symbol.operator: "([~^.:;,+*|=!\\%]|<|>|/|-|&)"
|
||||
|
||||
# parentheses
|
||||
- symbol.brackets: "([(){}]|\\[|\\])"
|
||||
|
||||
# numbers
|
||||
- constant.number: "\\b[0-9](_?[0-9])*(\\.([0-9](_?[0-9])*)?)?(e[0-9](_?[0-9])*)?\\b" # decimal
|
||||
- constant.number: "\\b0b(_?[01])+\\b" # bin
|
||||
- constant.number: "\\b0o(_?[0-7])+\\b" # oct
|
||||
- constant.number: "\\b0x(_?[0-9a-f])+\\b" # hex
|
||||
|
||||
- constant.bool.none: "\\b(None)\\b"
|
||||
- constant.bool.true: "\\b(True)\\b"
|
||||
- constant.bool.false: "\\b(False)\\b"
|
||||
|
||||
# strings
|
||||
- constant.string:
|
||||
start: "\""
|
||||
end: "(\"|$)"
|
||||
skip: "\\\\."
|
||||
rules: []
|
||||
- constant.string:
|
||||
start: "'"
|
||||
end: "('|$)"
|
||||
skip: "\\\\."
|
||||
rules: []
|
||||
|
||||
- comment:
|
||||
start: "#"
|
||||
end: "$"
|
||||
rules: []
|
94
runtime/syntax/log.yaml
Normal file
94
runtime/syntax/log.yaml
Normal file
|
@ -0,0 +1,94 @@
|
|||
filetype: log
|
||||
|
||||
detect:
|
||||
filename: "(\\.log|log\\.txt)$"
|
||||
|
||||
rules:
|
||||
- diff-modified: "\\b(WARN(ING)?|[Ww]arn(ing)?|w(r)?n|w|W/)\\b"
|
||||
- diff-modified: "\\b(CRITICAL|[Cc]ritical)\\b"
|
||||
|
||||
- constant: "\\b(INFO(RMATION)?|[Ii]nfo(rmation)?|[Ii]n(f)?|i|I/)\\b"
|
||||
- constant: "\\b(DEBUG|[Dd]ebug|dbug|dbg|de|d|D/)\\b"
|
||||
- constant: "\\b(VERBOSE|[Vv]erbose|V/)\\b"
|
||||
- constant: "\\b(ALERT|[Aa]lert)\\b"
|
||||
|
||||
- preproc: "\\b(TRACE|Trace|NOTICE|VERBOSE|verb|vrb|vb|v)\\b"
|
||||
|
||||
- gutter-error: "\\b(ERROR|[Ee]rr(or)?|[Ee]r(or)?|e|E\\x2F)\\b"
|
||||
- gutter-error: "\\b(FATAL|[Ff]atal)\\b"
|
||||
- gutter-error: "\\b(EMERGENCY|[Ee]mergency)\\b"
|
||||
- gutter-error: "\\b(FAIL(URE)?|[Ff]ail(ure)?)\\b"
|
||||
|
||||
# constants
|
||||
- constant.bool.true: "\\b(YES|yes|Y|y|ON|on|TRUE|True|true)\\b"
|
||||
- constant.bool.false: "\\b(NO|no|N|n|OFF|off|FALSE|False|false)\\b"
|
||||
- constant.bool.false: "\\b(None|null|nil)\\b"
|
||||
|
||||
# numbers
|
||||
- constant.number: "\\b[0-9](_?[0-9])*(\\.([0-9](_?[0-9])*)?)?(e[0-9](_?[0-9])*)?\\b" # decimal
|
||||
- constant.number: "\\b0b(_?[01])+\\b" # bin
|
||||
- constant.number: "\\b0o(_?[0-7])+\\b" # oct
|
||||
- constant.number: "\\b0x(_?[0-9a-f])+\\b" # hex
|
||||
|
||||
# operators
|
||||
- symbol.operator: "([~^.:;,+*|=!\\%]|<|>|/|-|&)"
|
||||
|
||||
# parentheses
|
||||
- symbol.brackets: "([(){}]|\\[|\\])"
|
||||
|
||||
# string
|
||||
- constant.string:
|
||||
start: "\""
|
||||
end: "(\"|$)"
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "\\\\."
|
||||
|
||||
- constant.string:
|
||||
start: "'"
|
||||
end: "('|$)"
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "\\\\."
|
||||
|
||||
# file
|
||||
- preproc: "\\b(FILE|File|file)\\b"
|
||||
|
||||
# time
|
||||
- identifier: "\\b((([Mm]on|[Tt]ues|[Ww]ed(nes)?|[Tt]hur(s)?|[Ff]ri|[Ss]at(ur)?|[Ss]un)(day)?\\s)?([Jj]an(uary)?|[Ff]eb(ruary)?|[Mm]ar(ch)?|[Aa]pr(il)?|[Mm]ay|[Jj]un(e)?|[Jj]ul(y)?|[Aa]ug(ust)?|[Aa]go|[Ss]ep(tember)?|[Oo]ct(ober)?|[Nn]ov(ember)?|[Dd]ec(ember)?)\\s\\d{1,2},?(\\s\\d{4})?)\\b" # date
|
||||
- identifier: "\\b(\\d{2,4}[-/\\.]?\\d{2,3}[-/\\.]?\\d{2,4})\\b" # date
|
||||
- identifier: "\\b(\\d{2}:\\d{2}(:\\d{2})?([\\.,]?\\d{1,8}[\\.\\+,]?\\d{1,8}?)?([\\.\\+,]?\\d{1,8}[\\.\\+,]?\\d{1,8}?)?([\\.\\+,]?\\d{1,8}?)?(\\s-\\d{0,4})?)\\b" # time
|
||||
- identifier: "^([0-2][0-9][0-2][0-9][-/]?[0-9][0-9][-/]?[0-9][0-9])"
|
||||
# - identifier: "^([0-2][0-9][0-2][0-9][-/]?[0-9][0-9][-/]?[0-9][0-9]\\s[0-9][0-9]:[0-9][0-9](:[0-9][0-9])?(\\.?[0-9][0-9][0-9])?)"
|
||||
- identifier: "^(\\d{4}[-/]?\\d{2}[-/]?\\d{2}\\s\\d{2}:\\d{2}(:\\d{2})?(\\.?\\d{2,8})?)"
|
||||
- identifier: "^([0-2][0-9]|[0-2]-?[0-9][0-9]-?[0-9][0-9])\\-([0-1][0-9])\\-([0-3][0-9]) ([0-2][0-9])\\:([0-5][0-9])\\:([0-5][0-9]),([0-9][0-9][0-9])"
|
||||
# ISO 8601:2004(E)
|
||||
- identifier: ""
|
||||
# Complete precision:
|
||||
- identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d:[0-5]\\d|Z))"
|
||||
# No milliseconds:
|
||||
- identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))"
|
||||
# No Seconds:
|
||||
- identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))"
|
||||
# Putting it all together:
|
||||
- identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))"
|
||||
# Complete precision:
|
||||
- identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+)"
|
||||
# No milliseconds
|
||||
- identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d)"
|
||||
# No Seconds
|
||||
- identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d)"
|
||||
# Putting it all together
|
||||
- identifier: "^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+)|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d)|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d)"
|
||||
|
||||
# link
|
||||
- constant.string.url:
|
||||
start: "https?://"
|
||||
end: "\\s"
|
||||
rules: []
|
||||
|
||||
# path
|
||||
# - constant.string.url: "\\b(.+)/([^/]+)\\b" # linux
|
||||
# - constant.string.url: "\\b(^[a-zA-Z]:)\\b" # windowns
|
||||
|
||||
- diff-modified: "([Cc]ommit:)\\s\\w+\\[\\w+]"
|
6
runtime/syntax/msbuild.yaml
Normal file
6
runtime/syntax/msbuild.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
filetype: msbuild
|
||||
detect:
|
||||
filename: "\\.(.*proj|props|targets|tasks)$"
|
||||
|
||||
rules:
|
||||
- include: "xml"
|
|
@ -39,7 +39,7 @@ rules:
|
|||
# Coreutils commands
|
||||
- type: "\\b(base64|basename|cat|chcon|chgrp|chmod|chown|chroot|cksum|comm|cp|csplit|cut|date|dd|df|dir|dircolors|dirname|du|env|expand|expr|factor|false|fmt|fold|head|hostid|id|install|join|link|ln|logname|ls|md5sum|mkdir|mkfifo|mknod|mktemp|mv|nice|nl|nohup|nproc|numfmt|od|paste|pathchk|pinky|pr|printenv|printf|ptx|pwd|readlink|realpath|rm|rmdir|runcon|seq|(sha1|sha224|sha256|sha384|sha512)sum|shred|shuf|sleep|sort|split|stat|stdbuf|stty|sum|sync|tac|tail|tee|test|time|timeout|touch|tr|true|truncate|tsort|tty|uname|unexpand|uniq|unlink|users|vdir|wc|who|whoami|yes)\\b"
|
||||
# Conditional flags
|
||||
- statement: " (-[A-Za-z]+|--[a-z]+)"
|
||||
- statement: "\\s+(-[A-Za-z]+|--[a-z]+)"
|
||||
|
||||
- identifier: "\\$\\{[0-9A-Za-z_:!%&=+#~@*^$?, .\\-\\/\\[\\]]+\\}"
|
||||
- identifier: "\\$[0-9A-Za-z_:!%&=+#~@*^$?,\\-\\[\\]]+"
|
||||
|
|
Loading…
Reference in a new issue