mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-31 15:17:15 +09:00
Compare commits
92 Commits
v2.0.7
...
tcell2.0.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bc498e625 | ||
|
|
346c10f20e | ||
|
|
86df9ad3b0 | ||
|
|
0851499130 | ||
|
|
88c95c8fae | ||
|
|
aaac60a78d | ||
|
|
ab6ce444a7 | ||
|
|
965e43ebf1 | ||
|
|
f2613eeb3b | ||
|
|
6d13710d93 | ||
|
|
7a3d1e6e30 | ||
|
|
0487db8b99 | ||
|
|
cd7ab640c5 | ||
|
|
a1651aec2f | ||
|
|
c960c93a83 | ||
|
|
f1aaa30743 | ||
|
|
9ed3525076 | ||
|
|
1f73f8587c | ||
|
|
c5798b5b8c | ||
|
|
6f949fe985 | ||
|
|
2d5fc15990 | ||
|
|
3c00d20ccc | ||
|
|
ae114a766c | ||
|
|
6761bdf8aa | ||
|
|
c014af9280 | ||
|
|
de8f4bf72f | ||
|
|
975e78d9c0 | ||
|
|
8aadf6af65 | ||
|
|
6c694a1db4 | ||
|
|
261eb41912 | ||
|
|
d7abc8f100 | ||
|
|
a36ab0188a | ||
|
|
ba98b558d9 | ||
|
|
c21b85929f | ||
|
|
c1e0e1d3b6 | ||
|
|
c3a17a71be | ||
|
|
120cd02b30 | ||
|
|
cf35b8021c | ||
|
|
fcc2fea684 | ||
|
|
84f7248943 | ||
|
|
e80f899480 | ||
|
|
dd37ad5ce4 | ||
|
|
c2b58fe861 | ||
|
|
ef5b21c861 | ||
|
|
54c23cae72 | ||
|
|
06a5f1a62d | ||
|
|
3321e844fc | ||
|
|
b5ce418201 | ||
|
|
57a3927f02 | ||
|
|
e4f7f80862 | ||
|
|
2fbeb40bf0 | ||
|
|
ecde9a53d7 | ||
|
|
299ba2fe97 | ||
|
|
6b7c04b421 | ||
|
|
83b3efc9de | ||
|
|
c13acf6b19 | ||
|
|
3b34a021e3 | ||
|
|
4c21808c6c | ||
|
|
1e5f8c020e | ||
|
|
3fb5a7053f | ||
|
|
7a5f7e443a | ||
|
|
7df04a58eb | ||
|
|
f3b21362f3 | ||
|
|
95fea064b0 | ||
|
|
5d230754a8 | ||
|
|
19067a9bf0 | ||
|
|
298fa40f90 | ||
|
|
23162f7a34 | ||
|
|
92e9060bcd | ||
|
|
b2620eb68c | ||
|
|
a424a0dca1 | ||
|
|
cfcb2e4577 | ||
|
|
95eb8eb9dd | ||
|
|
882bd8ad1f | ||
|
|
c0907bb58e | ||
|
|
225b24f356 | ||
|
|
8742674197 | ||
|
|
530041ac70 | ||
|
|
49786cf8c3 | ||
|
|
e8ba143144 | ||
|
|
e0dc018f66 | ||
|
|
cf63a68c8b | ||
|
|
fc3dd9a62f | ||
|
|
6dd92faf1a | ||
|
|
c26a365f5c | ||
|
|
08f0345d48 | ||
|
|
7ba484519e | ||
|
|
4341d536af | ||
|
|
8df921cf96 | ||
|
|
067f08c56e | ||
|
|
ad16f1756b | ||
|
|
fcfec28d79 |
@@ -5,7 +5,7 @@
|
||||
[](https://github.com/zyedidia/micro/releases)
|
||||
[](https://github.com/zyedidia/micro/blob/master/LICENSE)
|
||||
[](https://gitter.im/zyedidia/micro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://build.snapcraft.io/user/zyedidia/micro)
|
||||
[](https://snapcraft.io/micro)
|
||||
|
||||
**micro** is a terminal-based text editor that aims to be easy to use and intuitive, while also taking advantage of the capabilities
|
||||
of modern terminals. It comes as a single, batteries-included, static binary with no dependencies; you can download and use it right now!
|
||||
@@ -17,7 +17,7 @@ Here is a picture of micro editing its source code.
|
||||
|
||||

|
||||
|
||||
To see more screenshots of micro, showcasing some of the default color schemes, see [here](http://zbyedidia.webfactional.com/micro/screenshots.html).
|
||||
To see more screenshots of micro, showcasing some of the default color schemes, see [here](https://micro-editor.github.io).
|
||||
|
||||
You can also check out the website for Micro at https://micro-editor.github.io.
|
||||
|
||||
@@ -129,7 +129,7 @@ for other operating systems. These packages are not guaranteed to be up-to-date.
|
||||
* Linux: Available in distro-specific package managers.
|
||||
* `apt install micro` (Ubuntu 20.04 `focal`, and Debian `unstable | testing | buster-backports`). At the moment, this package (2.0.1-1) is outdated and has a known bug where debug mode is enabled.
|
||||
* `dnf install micro` (Fedora).
|
||||
* `yay -S micro` (Arch Linux).
|
||||
* `pacman -S micro` (Arch Linux).
|
||||
* `eopkg install micro` (Solus).
|
||||
* See [wiki](https://github.com/zyedidia/micro/wiki/Installing-Micro) for details about CRUX, Termux.
|
||||
* Windows: [Chocolatey](https://chocolatey.org) and [Scoop](https://github.com/lukesampson/scoop).
|
||||
|
||||
@@ -50,9 +50,7 @@ func luaImportMicro() *lua.LTable {
|
||||
ulua.L.SetField(pkg, "CurPane", luar.New(ulua.L, func() action.Pane {
|
||||
return action.MainTab().CurPane()
|
||||
}))
|
||||
ulua.L.SetField(pkg, "CurTab", luar.New(ulua.L, func() *action.Tab {
|
||||
return action.MainTab()
|
||||
}))
|
||||
ulua.L.SetField(pkg, "CurTab", luar.New(ulua.L, action.MainTab))
|
||||
ulua.L.SetField(pkg, "Tabs", luar.New(ulua.L, func() *action.TabList {
|
||||
return action.Tabs
|
||||
}))
|
||||
@@ -120,6 +118,9 @@ func luaImportMicroBuffer() *lua.LTable {
|
||||
ulua.L.SetField(pkg, "Loc", luar.New(ulua.L, func(x, y int) buffer.Loc {
|
||||
return buffer.Loc{x, y}
|
||||
}))
|
||||
ulua.L.SetField(pkg, "SLoc", luar.New(ulua.L, func(line, row int) display.SLoc {
|
||||
return display.SLoc{line, row}
|
||||
}))
|
||||
ulua.L.SetField(pkg, "BTDefault", luar.New(ulua.L, buffer.BTDefault.Kind))
|
||||
ulua.L.SetField(pkg, "BTHelp", luar.New(ulua.L, buffer.BTHelp.Kind))
|
||||
ulua.L.SetField(pkg, "BTLog", luar.New(ulua.L, buffer.BTLog.Kind))
|
||||
@@ -146,6 +147,9 @@ func luaImportMicroUtil() *lua.LTable {
|
||||
ulua.L.SetField(pkg, "GetLeadingWhitespace", luar.New(ulua.L, util.LuaGetLeadingWhitespace))
|
||||
ulua.L.SetField(pkg, "IsWordChar", luar.New(ulua.L, util.LuaIsWordChar))
|
||||
ulua.L.SetField(pkg, "String", luar.New(ulua.L, util.String))
|
||||
ulua.L.SetField(pkg, "Unzip", luar.New(ulua.L, util.Unzip))
|
||||
ulua.L.SetField(pkg, "Version", luar.New(ulua.L, util.Version))
|
||||
ulua.L.SetField(pkg, "SemVersion", luar.New(ulua.L, util.SemVersion))
|
||||
ulua.L.SetField(pkg, "CharacterCountInString", luar.New(ulua.L, util.CharacterCountInString))
|
||||
ulua.L.SetField(pkg, "RuneStr", luar.New(ulua.L, func(r rune) string {
|
||||
return string(r)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
@@ -39,6 +40,9 @@ var (
|
||||
flagPlugin = flag.String("plugin", "", "Plugin command")
|
||||
flagClean = flag.Bool("clean", false, "Clean configuration directory")
|
||||
optionFlags map[string]*string
|
||||
|
||||
sigterm chan os.Signal
|
||||
sighup chan os.Signal
|
||||
)
|
||||
|
||||
func InitFlags() {
|
||||
@@ -271,23 +275,10 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Kill, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
<-c
|
||||
|
||||
for _, b := range buffer.OpenBuffers {
|
||||
if !b.Modified() {
|
||||
b.Fini()
|
||||
}
|
||||
}
|
||||
|
||||
if screen.Screen != nil {
|
||||
screen.Screen.Fini()
|
||||
}
|
||||
os.Exit(0)
|
||||
}()
|
||||
sigterm = make(chan os.Signal, 1)
|
||||
sighup = make(chan os.Signal, 1)
|
||||
signal.Notify(sigterm, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
|
||||
signal.Notify(sighup, syscall.SIGHUP)
|
||||
|
||||
m := clipboard.SetMethod(config.GetGlobalOption("clipboard").(string))
|
||||
clipErr := clipboard.Initialize(m)
|
||||
@@ -351,7 +342,12 @@ func main() {
|
||||
}
|
||||
|
||||
if clipErr != nil {
|
||||
action.InfoBar.Error(clipErr, " or change 'clipboard' option")
|
||||
log.Println(clipErr, " or change 'clipboard' option")
|
||||
}
|
||||
|
||||
if a := config.GetGlobalOption("autosave").(float64); a > 0 {
|
||||
config.SetAutoTime(int(a))
|
||||
config.StartAutoSave()
|
||||
}
|
||||
|
||||
screen.Events = make(chan tcell.Event)
|
||||
@@ -421,6 +417,24 @@ func DoEvent() {
|
||||
for len(screen.DrawChan()) > 0 {
|
||||
<-screen.DrawChan()
|
||||
}
|
||||
case <-sighup:
|
||||
for _, b := range buffer.OpenBuffers {
|
||||
if !b.Modified() {
|
||||
b.Fini()
|
||||
}
|
||||
}
|
||||
os.Exit(0)
|
||||
case <-sigterm:
|
||||
for _, b := range buffer.OpenBuffers {
|
||||
if !b.Modified() {
|
||||
b.Fini()
|
||||
}
|
||||
}
|
||||
|
||||
if screen.Screen != nil {
|
||||
screen.Screen.Fini()
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
ulua.Lock.Lock()
|
||||
|
||||
4
go.mod
4
go.mod
@@ -17,7 +17,7 @@ require (
|
||||
github.com/zyedidia/highlight v0.0.0-20170330143449-201131ce5cf5
|
||||
github.com/zyedidia/json5 v0.0.0-20200102012142-2da050b1a98d
|
||||
github.com/zyedidia/pty v2.0.0+incompatible // indirect
|
||||
github.com/zyedidia/tcell/v2 v2.0.2
|
||||
github.com/zyedidia/tcell/v2 v2.0.7
|
||||
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415
|
||||
golang.org/x/text v0.3.2
|
||||
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||
@@ -27,6 +27,6 @@ require (
|
||||
|
||||
replace github.com/kballard/go-shellquote => github.com/zyedidia/go-shellquote v0.0.0-20200613203517-eccd813c0655
|
||||
|
||||
replace github.com/mattn/go-runewidth => github.com/p-e-w/go-runewidth v0.0.10-0.20200613030200-3e1705c5c059
|
||||
replace github.com/mattn/go-runewidth => github.com/zyedidia/go-runewidth v0.0.12
|
||||
|
||||
go 1.11
|
||||
|
||||
10
go.sum
10
go.sum
@@ -27,6 +27,8 @@ github.com/p-e-w/go-runewidth v0.0.10-0.20200613030200-3e1705c5c059 h1:/+h2b6i15
|
||||
github.com/p-e-w/go-runewidth v0.0.10-0.20200613030200-3e1705c5c059/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff h1:+6NUiITWwE5q1KO6SAfUX918c+Tab0+tGAM/mtdlUyA=
|
||||
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
@@ -44,6 +46,8 @@ github.com/zyedidia/clipboard v1.0.3 h1:F/nCDVYMdbDWTmY8s8cJl0tnwX32q96IF09JHM14
|
||||
github.com/zyedidia/clipboard v1.0.3/go.mod h1:zykFnZUXX0ErxqvYLUFEq7QDJKId8rmh2FgD0/Y8cjA=
|
||||
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3 h1:oMHjjTLfGXVuyOQBYj5/td9WC0mw4g1xDBPovIqmHew=
|
||||
github.com/zyedidia/glob v0.0.0-20170209203856-dd4023a66dc3/go.mod h1:YKbIYP//Eln8eDgAJGI3IDvR3s4Tv9Z9TGIOumiyQ5c=
|
||||
github.com/zyedidia/go-runewidth v0.0.12 h1:aHWj8qL3aH7caRzoPBJXe1pEaZBXHpKtfTuiBo5p74Q=
|
||||
github.com/zyedidia/go-runewidth v0.0.12/go.mod h1:vF8djYdLmG8BJaUZ4CznFYCJ3pFR8m4B4VinTvTTarU=
|
||||
github.com/zyedidia/go-shellquote v0.0.0-20200613203517-eccd813c0655 h1:Z3RhH6hvcSx7eX6Q/pP6YVsgea/1eMDG99vtWwi3nK4=
|
||||
github.com/zyedidia/go-shellquote v0.0.0-20200613203517-eccd813c0655/go.mod h1:1sTqqO+kcYzZp43M5VsJe1tns9IzlSeC9jB6c2+o/5Y=
|
||||
github.com/zyedidia/highlight v0.0.0-20170330143449-201131ce5cf5 h1:Zs6mpwXvlqpF9zHl5XaN0p5V4J9XvP+WBuiuXyIgqvc=
|
||||
@@ -54,8 +58,10 @@ github.com/zyedidia/poller v1.0.1 h1:Tt9S3AxAjXwWGNiC2TUdRJkQDZSzCBNVQ4xXiQ7440s
|
||||
github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE=
|
||||
github.com/zyedidia/pty v2.0.0+incompatible h1:Ou5vXL6tvjst+RV8sUFISbuKDnUJPhnpygApMFGweqw=
|
||||
github.com/zyedidia/pty v2.0.0+incompatible/go.mod h1:4y9l9yJZNxRa7GB/fB+mmDmGkG3CqmzLf4vUxGGotEA=
|
||||
github.com/zyedidia/tcell/v2 v2.0.2 h1:Zuf6vic3Yj2tFMXHCGOGL4WjqR3BwG30mOdwEz+ScxM=
|
||||
github.com/zyedidia/tcell/v2 v2.0.2/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws=
|
||||
github.com/zyedidia/tcell/v2 v2.0.6 h1:v0GoNpPYJ+Wbd1RiSL09SUFzoq4eVKTuT5awbW6aqGs=
|
||||
github.com/zyedidia/tcell/v2 v2.0.6/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws=
|
||||
github.com/zyedidia/tcell/v2 v2.0.7 h1:kFzCRq9jgx5lOXBT8fVZidbTgVuX0ws++aMCj/MTCYY=
|
||||
github.com/zyedidia/tcell/v2 v2.0.7/go.mod h1:i4NNlquIQXFeNecrOgxDQQJdu+7LmTi3g62asvmwUws=
|
||||
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415 h1:752dTQ5OatJ9M5ULK2+9lor+nzyZz+LYDo3WGngg3Rc=
|
||||
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415/go.mod h1:8leT8G0Cm8NoJHdrrKHyR9MirWoF4YW7pZh06B6H+1E=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"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"
|
||||
"github.com/zyedidia/micro/v2/internal/screen"
|
||||
"github.com/zyedidia/micro/v2/internal/shell"
|
||||
"github.com/zyedidia/micro/v2/internal/util"
|
||||
@@ -19,21 +20,26 @@ import (
|
||||
// ScrollUp is not an action
|
||||
func (h *BufPane) ScrollUp(n int) {
|
||||
v := h.GetView()
|
||||
if v.StartLine >= n {
|
||||
v.StartLine -= n
|
||||
h.SetView(v)
|
||||
} else {
|
||||
v.StartLine = 0
|
||||
}
|
||||
v.StartLine = h.Scroll(v.StartLine, -n)
|
||||
h.SetView(v)
|
||||
}
|
||||
|
||||
// ScrollDown is not an action
|
||||
func (h *BufPane) ScrollDown(n int) {
|
||||
v := h.GetView()
|
||||
if v.StartLine <= h.Buf.LinesNum()-1-n {
|
||||
v.StartLine += n
|
||||
h.SetView(v)
|
||||
v.StartLine = h.Scroll(v.StartLine, n)
|
||||
h.SetView(v)
|
||||
}
|
||||
|
||||
// If the user has scrolled past the last line, ScrollAdjust can be used
|
||||
// to shift the view so that the last line is at the bottom
|
||||
func (h *BufPane) ScrollAdjust() {
|
||||
v := h.GetView()
|
||||
end := h.SLocFromLoc(h.Buf.End())
|
||||
if h.Diff(v.StartLine, end) < h.BufView().Height-1 {
|
||||
v.StartLine = h.Scroll(end, -h.BufView().Height+1)
|
||||
}
|
||||
h.SetView(v)
|
||||
}
|
||||
|
||||
// MousePress is the event that should happen when a normal click happens
|
||||
@@ -111,22 +117,55 @@ func (h *BufPane) ScrollDownAction() bool {
|
||||
// Center centers the view on the cursor
|
||||
func (h *BufPane) Center() bool {
|
||||
v := h.GetView()
|
||||
v.StartLine = h.Cursor.Y - v.Height/2
|
||||
if v.StartLine+v.Height > h.Buf.LinesNum() {
|
||||
v.StartLine = h.Buf.LinesNum() - v.Height
|
||||
}
|
||||
if v.StartLine < 0 {
|
||||
v.StartLine = 0
|
||||
}
|
||||
v.StartLine = h.Scroll(h.SLocFromLoc(h.Cursor.Loc), -h.BufView().Height/2)
|
||||
h.SetView(v)
|
||||
h.Relocate()
|
||||
h.ScrollAdjust()
|
||||
return true
|
||||
}
|
||||
|
||||
// MoveCursorUp is not an action
|
||||
func (h *BufPane) MoveCursorUp(n int) {
|
||||
if !h.Buf.Settings["softwrap"].(bool) {
|
||||
h.Cursor.UpN(n)
|
||||
} else {
|
||||
vloc := h.VLocFromLoc(h.Cursor.Loc)
|
||||
sloc := h.Scroll(vloc.SLoc, -n)
|
||||
if sloc == vloc.SLoc {
|
||||
// we are at the beginning of buffer
|
||||
h.Cursor.Loc = h.Buf.Start()
|
||||
h.Cursor.LastVisualX = 0
|
||||
} else {
|
||||
vloc.SLoc = sloc
|
||||
vloc.VisualX = h.Cursor.LastVisualX
|
||||
h.Cursor.Loc = h.LocFromVLoc(vloc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MoveCursorDown is not an action
|
||||
func (h *BufPane) MoveCursorDown(n int) {
|
||||
if !h.Buf.Settings["softwrap"].(bool) {
|
||||
h.Cursor.DownN(n)
|
||||
} else {
|
||||
vloc := h.VLocFromLoc(h.Cursor.Loc)
|
||||
sloc := h.Scroll(vloc.SLoc, n)
|
||||
if sloc == vloc.SLoc {
|
||||
// we are at the end of buffer
|
||||
h.Cursor.Loc = h.Buf.End()
|
||||
vloc = h.VLocFromLoc(h.Cursor.Loc)
|
||||
h.Cursor.LastVisualX = vloc.VisualX
|
||||
} else {
|
||||
vloc.SLoc = sloc
|
||||
vloc.VisualX = h.Cursor.LastVisualX
|
||||
h.Cursor.Loc = h.LocFromVLoc(vloc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CursorUp moves the cursor up
|
||||
func (h *BufPane) CursorUp() bool {
|
||||
h.Cursor.Deselect(true)
|
||||
h.Cursor.Up()
|
||||
h.MoveCursorUp(1)
|
||||
h.Relocate()
|
||||
return true
|
||||
}
|
||||
@@ -134,7 +173,7 @@ func (h *BufPane) CursorUp() bool {
|
||||
// CursorDown moves the cursor down
|
||||
func (h *BufPane) CursorDown() bool {
|
||||
h.Cursor.Deselect(true)
|
||||
h.Cursor.Down()
|
||||
h.MoveCursorDown(1)
|
||||
h.Relocate()
|
||||
return true
|
||||
}
|
||||
@@ -212,7 +251,7 @@ func (h *BufPane) SelectUp() bool {
|
||||
if !h.Cursor.HasSelection() {
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||
}
|
||||
h.Cursor.Up()
|
||||
h.MoveCursorUp(1)
|
||||
h.Cursor.SelectTo(h.Cursor.Loc)
|
||||
h.Relocate()
|
||||
return true
|
||||
@@ -223,7 +262,7 @@ func (h *BufPane) SelectDown() bool {
|
||||
if !h.Cursor.HasSelection() {
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||
}
|
||||
h.Cursor.Down()
|
||||
h.MoveCursorDown(1)
|
||||
h.Cursor.SelectTo(h.Cursor.Loc)
|
||||
h.Relocate()
|
||||
return true
|
||||
@@ -847,21 +886,24 @@ func (h *BufPane) find(useRegex bool) bool {
|
||||
if useRegex {
|
||||
prompt = "Find (regex): "
|
||||
}
|
||||
InfoBar.Prompt(prompt, "", "Find", func(resp string) {
|
||||
// Event callback
|
||||
match, found, _ := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, useRegex)
|
||||
if found {
|
||||
h.Cursor.SetSelectionStart(match[0])
|
||||
h.Cursor.SetSelectionEnd(match[1])
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0]
|
||||
h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1]
|
||||
h.Cursor.GotoLoc(match[1])
|
||||
} else {
|
||||
h.Cursor.GotoLoc(h.searchOrig)
|
||||
h.Cursor.ResetSelection()
|
||||
var eventCallback func(resp string)
|
||||
if h.Buf.Settings["incsearch"].(bool) {
|
||||
eventCallback = func(resp string) {
|
||||
match, found, _ := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, useRegex)
|
||||
if found {
|
||||
h.Cursor.SetSelectionStart(match[0])
|
||||
h.Cursor.SetSelectionEnd(match[1])
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.CurSelection[0]
|
||||
h.Cursor.OrigSelection[1] = h.Cursor.CurSelection[1]
|
||||
h.Cursor.GotoLoc(match[1])
|
||||
} else {
|
||||
h.Cursor.GotoLoc(h.searchOrig)
|
||||
h.Cursor.ResetSelection()
|
||||
}
|
||||
h.Relocate()
|
||||
}
|
||||
h.Relocate()
|
||||
}, func(resp string, canceled bool) {
|
||||
}
|
||||
InfoBar.Prompt(prompt, "", "Find", eventCallback, func(resp string, canceled bool) {
|
||||
// Finished callback
|
||||
if !canceled {
|
||||
match, found, err := h.Buf.FindNext(resp, h.Buf.Start(), h.Buf.End(), h.searchOrig, true, useRegex)
|
||||
@@ -991,7 +1033,7 @@ func (h *BufPane) CutLine() bool {
|
||||
if !h.Cursor.HasSelection() {
|
||||
return false
|
||||
}
|
||||
if h.freshClip == true {
|
||||
if h.freshClip {
|
||||
if h.Cursor.HasSelection() {
|
||||
if clip, err := clipboard.Read(clipboard.ClipboardReg); err != nil {
|
||||
InfoBar.Error(err)
|
||||
@@ -999,7 +1041,7 @@ func (h *BufPane) CutLine() bool {
|
||||
clipboard.WriteMulti(clip+string(h.Cursor.GetSelection()), clipboard.ClipboardReg, h.Cursor.Num, h.Buf.NumCursors())
|
||||
}
|
||||
}
|
||||
} else if time.Since(h.lastCutTime)/time.Second > 10*time.Second || h.freshClip == false {
|
||||
} else if time.Since(h.lastCutTime)/time.Second > 10*time.Second || !h.freshClip {
|
||||
h.Copy()
|
||||
}
|
||||
h.freshClip = true
|
||||
@@ -1166,7 +1208,7 @@ 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.Replace(clip, "\n", "\n"+string(leadingWS), -1)
|
||||
clip = strings.ReplaceAll(clip, "\n", "\n"+string(leadingWS))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1195,6 +1237,7 @@ func (h *BufPane) JumpToMatchingBrace() bool {
|
||||
} else {
|
||||
h.Cursor.GotoLoc(matchingBrace.Move(1, h.Buf))
|
||||
}
|
||||
break
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
@@ -1239,45 +1282,29 @@ func (h *BufPane) JumpLine() bool {
|
||||
// Start moves the viewport to the start of the buffer
|
||||
func (h *BufPane) Start() bool {
|
||||
v := h.GetView()
|
||||
v.StartLine = 0
|
||||
v.StartLine = display.SLoc{0, 0}
|
||||
h.SetView(v)
|
||||
return true
|
||||
}
|
||||
|
||||
// End moves the viewport to the end of the buffer
|
||||
func (h *BufPane) End() bool {
|
||||
// TODO: softwrap problems?
|
||||
v := h.GetView()
|
||||
if v.Height > h.Buf.LinesNum() {
|
||||
v.StartLine = 0
|
||||
h.SetView(v)
|
||||
} else {
|
||||
v.StartLine = h.Buf.LinesNum() - v.Height
|
||||
h.SetView(v)
|
||||
}
|
||||
v.StartLine = h.Scroll(h.SLocFromLoc(h.Buf.End()), -h.BufView().Height+1)
|
||||
h.SetView(v)
|
||||
return true
|
||||
}
|
||||
|
||||
// PageUp scrolls the view up a page
|
||||
func (h *BufPane) PageUp() bool {
|
||||
v := h.GetView()
|
||||
if v.StartLine > v.Height {
|
||||
h.ScrollUp(v.Height)
|
||||
} else {
|
||||
v.StartLine = 0
|
||||
}
|
||||
h.SetView(v)
|
||||
h.ScrollUp(h.BufView().Height)
|
||||
return true
|
||||
}
|
||||
|
||||
// PageDown scrolls the view down a page
|
||||
func (h *BufPane) PageDown() bool {
|
||||
v := h.GetView()
|
||||
if h.Buf.LinesNum()-(v.StartLine+v.Height) > v.Height {
|
||||
h.ScrollDown(v.Height)
|
||||
} else if h.Buf.LinesNum() >= v.Height {
|
||||
v.StartLine = h.Buf.LinesNum() - v.Height
|
||||
}
|
||||
h.ScrollDown(h.BufView().Height)
|
||||
h.ScrollAdjust()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1286,7 +1313,7 @@ func (h *BufPane) SelectPageUp() bool {
|
||||
if !h.Cursor.HasSelection() {
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||
}
|
||||
h.Cursor.UpN(h.GetView().Height)
|
||||
h.MoveCursorUp(h.BufView().Height)
|
||||
h.Cursor.SelectTo(h.Cursor.Loc)
|
||||
h.Relocate()
|
||||
return true
|
||||
@@ -1297,7 +1324,7 @@ func (h *BufPane) SelectPageDown() bool {
|
||||
if !h.Cursor.HasSelection() {
|
||||
h.Cursor.OrigSelection[0] = h.Cursor.Loc
|
||||
}
|
||||
h.Cursor.DownN(h.GetView().Height)
|
||||
h.MoveCursorDown(h.BufView().Height)
|
||||
h.Cursor.SelectTo(h.Cursor.Loc)
|
||||
h.Relocate()
|
||||
return true
|
||||
@@ -1312,7 +1339,7 @@ func (h *BufPane) CursorPageUp() bool {
|
||||
h.Cursor.ResetSelection()
|
||||
h.Cursor.StoreVisualX()
|
||||
}
|
||||
h.Cursor.UpN(h.GetView().Height)
|
||||
h.MoveCursorUp(h.BufView().Height)
|
||||
h.Relocate()
|
||||
return true
|
||||
}
|
||||
@@ -1326,34 +1353,21 @@ func (h *BufPane) CursorPageDown() bool {
|
||||
h.Cursor.ResetSelection()
|
||||
h.Cursor.StoreVisualX()
|
||||
}
|
||||
h.Cursor.DownN(h.GetView().Height)
|
||||
h.MoveCursorDown(h.BufView().Height)
|
||||
h.Relocate()
|
||||
return true
|
||||
}
|
||||
|
||||
// HalfPageUp scrolls the view up half a page
|
||||
func (h *BufPane) HalfPageUp() bool {
|
||||
v := h.GetView()
|
||||
if v.StartLine > v.Height/2 {
|
||||
h.ScrollUp(v.Height / 2)
|
||||
} else {
|
||||
v.StartLine = 0
|
||||
}
|
||||
h.SetView(v)
|
||||
h.ScrollUp(h.BufView().Height / 2)
|
||||
return true
|
||||
}
|
||||
|
||||
// HalfPageDown scrolls the view down half a page
|
||||
func (h *BufPane) HalfPageDown() bool {
|
||||
v := h.GetView()
|
||||
if h.Buf.LinesNum()-(v.StartLine+v.Height) > v.Height/2 {
|
||||
h.ScrollDown(v.Height / 2)
|
||||
} else {
|
||||
if h.Buf.LinesNum() >= v.Height {
|
||||
v.StartLine = h.Buf.LinesNum() - v.Height
|
||||
}
|
||||
}
|
||||
h.SetView(v)
|
||||
h.ScrollDown(h.BufView().Height / 2)
|
||||
h.ScrollAdjust()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1452,39 +1466,43 @@ func (h *BufPane) ClearInfo() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ForceQuit closes the current tab or view even if there are unsaved changes
|
||||
// (no prompt)
|
||||
func (h *BufPane) ForceQuit() bool {
|
||||
h.Buf.Close()
|
||||
if len(MainTab().Panes) > 1 {
|
||||
h.Unsplit()
|
||||
} else if len(Tabs.List) > 1 {
|
||||
Tabs.RemoveTab(h.splitID)
|
||||
} else {
|
||||
screen.Screen.Fini()
|
||||
InfoBar.Close()
|
||||
runtime.Goexit()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Quit this will close the current tab or view that is open
|
||||
func (h *BufPane) Quit() bool {
|
||||
quit := func() {
|
||||
h.Buf.Close()
|
||||
if len(MainTab().Panes) > 1 {
|
||||
h.Unsplit()
|
||||
} else if len(Tabs.List) > 1 {
|
||||
Tabs.RemoveTab(h.splitID)
|
||||
} else {
|
||||
screen.Screen.Fini()
|
||||
InfoBar.Close()
|
||||
runtime.Goexit()
|
||||
}
|
||||
}
|
||||
if h.Buf.Modified() {
|
||||
if config.GlobalSettings["autosave"].(float64) > 0 {
|
||||
// autosave on means we automatically save when quitting
|
||||
h.SaveCB("Quit", func() {
|
||||
quit()
|
||||
h.ForceQuit()
|
||||
})
|
||||
} else {
|
||||
InfoBar.YNPrompt("Save changes to "+h.Buf.GetName()+" before closing? (y,n,esc)", func(yes, canceled bool) {
|
||||
if !canceled && !yes {
|
||||
quit()
|
||||
h.ForceQuit()
|
||||
} else if !canceled && yes {
|
||||
h.SaveCB("Quit", func() {
|
||||
quit()
|
||||
h.ForceQuit()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
quit()
|
||||
h.ForceQuit()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package action
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -61,7 +62,11 @@ func InitBindings() {
|
||||
case string:
|
||||
BindKey(k, val, Binder["buffer"])
|
||||
case map[string]interface{}:
|
||||
bind := Binder[k]
|
||||
bind, ok := Binder[k]
|
||||
if !ok || bind == nil {
|
||||
screen.TermMessage(fmt.Sprintf("%s is not a valid pane type", k))
|
||||
continue
|
||||
}
|
||||
for e, a := range val {
|
||||
s, ok := a.(string)
|
||||
if !ok {
|
||||
@@ -83,8 +88,6 @@ func BindKey(k, v string, bind func(e Event, a string)) {
|
||||
return
|
||||
}
|
||||
|
||||
config.Bindings[event.Name()] = v
|
||||
|
||||
bind(event, v)
|
||||
|
||||
// switch e := event.(type) {
|
||||
@@ -159,7 +162,7 @@ modSearch:
|
||||
}
|
||||
}
|
||||
|
||||
if len(k) == 0 {
|
||||
if k == "" {
|
||||
return KeyEvent{}, false
|
||||
}
|
||||
|
||||
@@ -240,7 +243,7 @@ func findEvent(k string) (Event, error) {
|
||||
// Returns true if the keybinding already existed and a possible error
|
||||
func TryBindKey(k, v string, overwrite bool) (bool, error) {
|
||||
var e error
|
||||
var parsed map[string]string
|
||||
var parsed map[string]interface{}
|
||||
|
||||
filename := filepath.Join(config.ConfigDir, "bindings.json")
|
||||
createBindingsIfNotExist(filename)
|
||||
@@ -290,7 +293,7 @@ func TryBindKey(k, v string, overwrite bool) (bool, error) {
|
||||
// UnbindKey removes the binding for a key from the bindings.json file
|
||||
func UnbindKey(k string) error {
|
||||
var e error
|
||||
var parsed map[string]string
|
||||
var parsed map[string]interface{}
|
||||
|
||||
filename := filepath.Join(config.ConfigDir, "bindings.json")
|
||||
createBindingsIfNotExist(filename)
|
||||
@@ -322,9 +325,9 @@ func UnbindKey(k string) error {
|
||||
defaults := DefaultBindings("buffer")
|
||||
if a, ok := defaults[k]; ok {
|
||||
BindKey(k, a, Binder["buffer"])
|
||||
} else if _, ok := config.Bindings[k]; ok {
|
||||
} else if _, ok := config.Bindings["buffer"][k]; ok {
|
||||
BufUnmap(key)
|
||||
delete(config.Bindings, k)
|
||||
delete(config.Bindings["buffer"], k)
|
||||
}
|
||||
|
||||
txt, _ := json.MarshalIndent(parsed, "", " ")
|
||||
|
||||
@@ -62,6 +62,8 @@ func LuaAction(fn string) func(*BufPane) bool {
|
||||
|
||||
// BufMapKey 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)
|
||||
@@ -664,6 +666,7 @@ var BufKeyActions = map[string]BufKeyAction{
|
||||
"Escape": (*BufPane).Escape,
|
||||
"Quit": (*BufPane).Quit,
|
||||
"QuitAll": (*BufPane).QuitAll,
|
||||
"ForceQuit": (*BufPane).ForceQuit,
|
||||
"AddTab": (*BufPane).AddTab,
|
||||
"PreviousTab": (*BufPane).PreviousTab,
|
||||
"NextTab": (*BufPane).NextTab,
|
||||
|
||||
@@ -646,7 +646,7 @@ func (h *BufPane) ShowKeyCmd(args []string) {
|
||||
InfoBar.Error(err)
|
||||
return
|
||||
}
|
||||
if action, ok := config.Bindings[event.Name()]; ok {
|
||||
if action, ok := config.Bindings["buffer"][event.Name()]; ok {
|
||||
InfoBar.Message(action)
|
||||
} else {
|
||||
InfoBar.Message(args[0], " has no binding")
|
||||
@@ -820,7 +820,7 @@ func (h *BufPane) ReplaceCmd(args []string) {
|
||||
searchLoc := h.Cursor.Loc
|
||||
var doReplacement func()
|
||||
doReplacement = func() {
|
||||
locs, found, err := h.Buf.FindNext(search, start, end, searchLoc, true, !noRegex)
|
||||
locs, found, err := h.Buf.FindNext(search, start, end, searchLoc, true, true)
|
||||
if err != nil {
|
||||
InfoBar.Error(err)
|
||||
return
|
||||
|
||||
@@ -21,13 +21,6 @@ func WriteLog(s string) {
|
||||
buffer.WriteLog(s)
|
||||
if LogBufPane != nil {
|
||||
LogBufPane.CursorEnd()
|
||||
v := LogBufPane.GetView()
|
||||
endY := buffer.LogBuf.End().Y
|
||||
|
||||
if endY > v.StartLine+v.Height {
|
||||
v.StartLine = buffer.LogBuf.End().Y - v.Height + 2
|
||||
LogBufPane.SetView(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,12 +30,4 @@ func WriteLog(s string) {
|
||||
func (h *BufPane) OpenLogBuf() {
|
||||
LogBufPane = h.HSplitBuf(buffer.LogBuf)
|
||||
LogBufPane.CursorEnd()
|
||||
|
||||
v := LogBufPane.GetView()
|
||||
endY := buffer.LogBuf.End().Y
|
||||
|
||||
if endY > v.StartLine+v.Height {
|
||||
v.StartLine = buffer.LogBuf.End().Y - v.Height + 2
|
||||
LogBufPane.SetView(v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
|
||||
"github.com/zyedidia/micro/v2/internal/buffer"
|
||||
"github.com/zyedidia/micro/v2/internal/config"
|
||||
"github.com/zyedidia/micro/v2/internal/display"
|
||||
"github.com/zyedidia/micro/v2/internal/info"
|
||||
"github.com/zyedidia/micro/v2/internal/util"
|
||||
@@ -21,6 +22,8 @@ func init() {
|
||||
}
|
||||
|
||||
func InfoMapEvent(k Event, action string) {
|
||||
config.Bindings["command"][k.Name()] = action
|
||||
|
||||
switch e := k.(type) {
|
||||
case KeyEvent, KeySequenceEvent, RawEvent:
|
||||
infoMapKey(e, action)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"runtime"
|
||||
|
||||
"github.com/zyedidia/micro/v2/internal/clipboard"
|
||||
"github.com/zyedidia/micro/v2/internal/config"
|
||||
"github.com/zyedidia/micro/v2/internal/display"
|
||||
"github.com/zyedidia/micro/v2/internal/screen"
|
||||
"github.com/zyedidia/micro/v2/internal/shell"
|
||||
@@ -28,6 +29,8 @@ func TermKeyActionGeneral(a TermKeyAction) PaneKeyAction {
|
||||
}
|
||||
|
||||
func TermMapEvent(k Event, action string) {
|
||||
config.Bindings["terminal"][k.Name()] = action
|
||||
|
||||
switch e := k.(type) {
|
||||
case KeyEvent, KeySequenceEvent, RawEvent:
|
||||
termMapKey(e, action)
|
||||
|
||||
@@ -54,8 +54,6 @@ func (b *Buffer) CycleAutocomplete(forward bool) {
|
||||
end := c.Loc
|
||||
if prevSuggestion < len(b.Suggestions) && prevSuggestion >= 0 {
|
||||
start = end.Move(-util.CharacterCountInString(b.Completions[prevSuggestion]), b)
|
||||
} else {
|
||||
// end = start.Move(1, b)
|
||||
}
|
||||
|
||||
b.Replace(start, end, b.Completions[b.CurSuggestion])
|
||||
@@ -71,11 +69,11 @@ func GetWord(b *Buffer) ([]byte, int) {
|
||||
l := b.LineBytes(c.Y)
|
||||
l = util.SliceStart(l, c.X)
|
||||
|
||||
if c.X == 0 || util.IsWhitespace(b.RuneAt(c.Loc)) {
|
||||
if c.X == 0 || util.IsWhitespace(b.RuneAt(c.Loc.Move(-1, b))) {
|
||||
return []byte{}, -1
|
||||
}
|
||||
|
||||
if util.IsNonAlphaNumeric(b.RuneAt(c.Loc)) {
|
||||
if util.IsNonAlphaNumeric(b.RuneAt(c.Loc.Move(-1, b))) {
|
||||
return []byte{}, c.X
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ func (b *Buffer) Backup() error {
|
||||
}
|
||||
|
||||
backupdir, err := util.ReplaceHome(b.Settings["backupdir"].(string))
|
||||
if len(backupdir) == 0 || err != nil {
|
||||
if backupdir == "" || err != nil {
|
||||
backupdir = filepath.Join(config.ConfigDir, "backups")
|
||||
}
|
||||
if _, err := os.Stat(backupdir); os.IsNotExist(err) {
|
||||
|
||||
@@ -189,6 +189,19 @@ type Buffer struct {
|
||||
cursors []*Cursor
|
||||
curCursor int
|
||||
StartCursor Loc
|
||||
|
||||
// OptionCallback is called after a buffer option value is changed.
|
||||
// The display module registers its OptionCallback to ensure the buffer window
|
||||
// is properly updated when needed. This is a workaround for the fact that
|
||||
// the buffer module cannot directly call the display's API (it would mean
|
||||
// a circular dependency between packages).
|
||||
OptionCallback func(option string, nativeValue interface{})
|
||||
|
||||
// The display module registers its own GetVisualX function for getting
|
||||
// the correct visual x location of a cursor when softwrap is used.
|
||||
// This is hacky. Maybe it would be better to move all the visual x logic
|
||||
// from buffer to display, but it would require rewriting a lot of code.
|
||||
GetVisualX func(loc Loc) int
|
||||
}
|
||||
|
||||
// NewBufferFromFileAtLoc opens a new buffer with a given cursor location
|
||||
@@ -212,23 +225,37 @@ func NewBufferFromFileAtLoc(path string, btype BufType, cursorLoc Loc) (*Buffer,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file, err := os.Open(filename)
|
||||
fileInfo, _ := os.Stat(filename)
|
||||
f, err := os.OpenFile(filename, os.O_WRONLY, 0)
|
||||
readonly := os.IsPermission(err)
|
||||
f.Close()
|
||||
|
||||
if err == nil && fileInfo.IsDir() {
|
||||
fileInfo, serr := os.Stat(filename)
|
||||
if serr != nil && !os.IsNotExist(serr) {
|
||||
return nil, serr
|
||||
}
|
||||
if serr == nil && fileInfo.IsDir() {
|
||||
return nil, errors.New("Error: " + filename + " is a directory and cannot be opened")
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
file, err := os.Open(filename)
|
||||
if err == nil {
|
||||
defer file.Close()
|
||||
}
|
||||
|
||||
var buf *Buffer
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// File does not exist -- create an empty buffer with that name
|
||||
buf = NewBufferFromString("", filename, btype)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
buf = NewBuffer(file, util.FSize(file), filename, cursorLoc, btype)
|
||||
}
|
||||
|
||||
if readonly {
|
||||
buf.SetOptionNative("readonly", true)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
@@ -279,16 +306,27 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
|
||||
b.AbsPath = absPath
|
||||
b.Path = path
|
||||
|
||||
// this is a little messy since we need to know some settings to read
|
||||
// the file properly, but some settings depend on the filetype, which
|
||||
// we don't know until reading the file. We first read the settings
|
||||
// into a local variable and then use that to determine the encoding,
|
||||
// readonly, and fileformat necessary for reading the file and
|
||||
// assigning the filetype.
|
||||
settings := config.DefaultCommonSettings()
|
||||
b.Settings = config.DefaultCommonSettings()
|
||||
for k, v := range config.GlobalSettings {
|
||||
if _, ok := config.DefaultGlobalOnlySettings[k]; !ok {
|
||||
// make sure setting is not global-only
|
||||
settings[k] = v
|
||||
b.Settings[k] = v
|
||||
}
|
||||
}
|
||||
config.InitLocalSettings(b.Settings, path)
|
||||
config.InitLocalSettings(settings, path)
|
||||
b.Settings["readonly"] = settings["readonly"]
|
||||
b.Settings["filetype"] = settings["filetype"]
|
||||
b.Settings["syntax"] = settings["syntax"]
|
||||
|
||||
enc, err := htmlindex.Get(b.Settings["encoding"].(string))
|
||||
enc, err := htmlindex.Get(settings["encoding"].(string))
|
||||
if err != nil {
|
||||
enc = unicode.UTF8
|
||||
b.Settings["encoding"] = "utf-8"
|
||||
@@ -304,7 +342,7 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
|
||||
if size == 0 {
|
||||
// for empty files, use the fileformat setting instead of
|
||||
// autodetection
|
||||
switch b.Settings["fileformat"] {
|
||||
switch settings["fileformat"] {
|
||||
case "unix":
|
||||
ff = FFUnix
|
||||
case "dos":
|
||||
@@ -341,12 +379,10 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
|
||||
|
||||
if startcursor.X != -1 && startcursor.Y != -1 {
|
||||
b.StartCursor = startcursor
|
||||
} else {
|
||||
if b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool) {
|
||||
err := b.Unserialize()
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
} else if b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool) {
|
||||
err := b.Unserialize()
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,16 +547,38 @@ func (b *Buffer) RuneAt(loc Loc) rune {
|
||||
for len(line) > 0 {
|
||||
r, _, size := util.DecodeCharacter(line)
|
||||
line = line[size:]
|
||||
i++
|
||||
|
||||
if i == loc.X {
|
||||
return r
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
return '\n'
|
||||
}
|
||||
|
||||
// WordAt returns the word around a given location in the buffer
|
||||
func (b *Buffer) WordAt(loc Loc) []byte {
|
||||
if len(b.LineBytes(loc.Y)) == 0 || !util.IsWordChar(b.RuneAt(loc)) {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
start := loc
|
||||
end := loc.Move(1, b)
|
||||
|
||||
for start.X > 0 && util.IsWordChar(b.RuneAt(start.Move(-1, b))) {
|
||||
start.X--
|
||||
}
|
||||
|
||||
lineLen := util.CharacterCount(b.LineBytes(loc.Y))
|
||||
for end.X < lineLen && util.IsWordChar(b.RuneAt(end)) {
|
||||
end.X++
|
||||
}
|
||||
|
||||
return b.Substr(start, end)
|
||||
}
|
||||
|
||||
// Modified returns if this buffer has been modified since
|
||||
// being opened
|
||||
func (b *Buffer) Modified() bool {
|
||||
@@ -610,6 +668,9 @@ func (b *Buffer) UpdateRules() {
|
||||
}
|
||||
|
||||
header, err = highlight.MakeHeaderYaml(data)
|
||||
if err != nil {
|
||||
screen.TermMessage("Error parsing header for syntax file " + f.Name() + ": " + err.Error())
|
||||
}
|
||||
file, err := highlight.ParseFile(data)
|
||||
if err != nil {
|
||||
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
||||
@@ -985,9 +1046,9 @@ func (b *Buffer) Retab() {
|
||||
ws := util.GetLeadingWhitespace(l)
|
||||
if len(ws) != 0 {
|
||||
if toSpaces {
|
||||
ws = bytes.Replace(ws, []byte{'\t'}, bytes.Repeat([]byte{' '}, tabsize), -1)
|
||||
ws = bytes.ReplaceAll(ws, []byte{'\t'}, bytes.Repeat([]byte{' '}, tabsize))
|
||||
} else {
|
||||
ws = bytes.Replace(ws, bytes.Repeat([]byte{' '}, tabsize), []byte{'\t'}, -1)
|
||||
ws = bytes.ReplaceAll(ws, bytes.Repeat([]byte{' '}, tabsize), []byte{'\t'})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,10 @@ func (c *Cursor) GotoLoc(l Loc) {
|
||||
|
||||
// GetVisualX returns the x value of the cursor in visual spaces
|
||||
func (c *Cursor) GetVisualX() int {
|
||||
if c.buf.GetVisualX != nil {
|
||||
return c.buf.GetVisualX(c.Loc)
|
||||
}
|
||||
|
||||
if c.X <= 0 {
|
||||
c.X = 0
|
||||
return 0
|
||||
|
||||
@@ -130,7 +130,7 @@ func NewLineArray(size uint64, endings FileFormat, reader io.Reader) *LineArray
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
la.lines = Append(la.lines, Line{
|
||||
data: data[:],
|
||||
data: data,
|
||||
state: nil,
|
||||
match: nil,
|
||||
rehighlight: false,
|
||||
|
||||
@@ -109,7 +109,7 @@ func (b *Buffer) saveToFile(filename string, withSudo bool) error {
|
||||
|
||||
if b.Settings["eofnewline"].(bool) {
|
||||
end := b.End()
|
||||
if b.RuneAt(Loc{end.X, end.Y}) != '\n' {
|
||||
if b.RuneAt(Loc{end.X - 1, end.Y}) != '\n' {
|
||||
b.insert(end, []byte{'\n'})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ func (b *Buffer) Unserialize() error {
|
||||
return nil
|
||||
}
|
||||
file, err := os.Open(filepath.Join(config.ConfigDir, "buffers", util.EscapePath(b.AbsPath)))
|
||||
defer file.Close()
|
||||
if err == nil {
|
||||
defer file.Close()
|
||||
var buffer SerializedBuffer
|
||||
decoder := gob.NewDecoder(file)
|
||||
err = decoder.Decode(&buffer)
|
||||
|
||||
@@ -41,6 +41,10 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error {
|
||||
b.Type.Readonly = nativeValue.(bool)
|
||||
}
|
||||
|
||||
if b.OptionCallback != nil {
|
||||
b.OptionCallback(option, nativeValue)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ func Initialize(m Method) error {
|
||||
case External:
|
||||
err = clipboard.Initialize()
|
||||
}
|
||||
if err != nil {
|
||||
CurrentMethod = Internal
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -31,14 +31,13 @@ func GetAutoTime() int {
|
||||
func StartAutoSave() {
|
||||
go func() {
|
||||
for {
|
||||
if autotime < 1 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Duration(autotime) * time.Second)
|
||||
// it's possible autotime was changed while sleeping
|
||||
if autotime < 1 {
|
||||
autolock.Lock()
|
||||
a := autotime
|
||||
autolock.Unlock()
|
||||
if a < 1 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Duration(a) * time.Second)
|
||||
Autosave <- true
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -121,8 +121,7 @@ func ParseColorscheme(text string) (map[string]tcell.Style, error) {
|
||||
func StringToStyle(str string) tcell.Style {
|
||||
var fg, bg string
|
||||
spaceSplit := strings.Split(str, " ")
|
||||
var split []string
|
||||
split = strings.Split(spaceSplit[len(spaceSplit)-1], ",")
|
||||
split := strings.Split(spaceSplit[len(spaceSplit)-1], ",")
|
||||
if len(split) > 1 {
|
||||
fg, bg = split[0], split[1]
|
||||
} else {
|
||||
@@ -209,47 +208,8 @@ func StringToColor(str string) tcell.Color {
|
||||
|
||||
// GetColor256 returns the tcell color for a number between 0 and 255
|
||||
func GetColor256(color int) tcell.Color {
|
||||
colors := []tcell.Color{tcell.ColorBlack, tcell.ColorMaroon, tcell.ColorGreen,
|
||||
tcell.ColorOlive, tcell.ColorNavy, tcell.ColorPurple,
|
||||
tcell.ColorTeal, tcell.ColorSilver, tcell.ColorGray,
|
||||
tcell.ColorRed, tcell.ColorLime, tcell.ColorYellow,
|
||||
tcell.ColorBlue, tcell.ColorFuchsia, tcell.ColorAqua,
|
||||
tcell.ColorWhite, tcell.Color16, tcell.Color17, tcell.Color18, tcell.Color19, tcell.Color20,
|
||||
tcell.Color21, tcell.Color22, tcell.Color23, tcell.Color24, tcell.Color25, tcell.Color26, tcell.Color27, tcell.Color28,
|
||||
tcell.Color29, tcell.Color30, tcell.Color31, tcell.Color32, tcell.Color33, tcell.Color34, tcell.Color35, tcell.Color36,
|
||||
tcell.Color37, tcell.Color38, tcell.Color39, tcell.Color40, tcell.Color41, tcell.Color42, tcell.Color43, tcell.Color44,
|
||||
tcell.Color45, tcell.Color46, tcell.Color47, tcell.Color48, tcell.Color49, tcell.Color50, tcell.Color51, tcell.Color52,
|
||||
tcell.Color53, tcell.Color54, tcell.Color55, tcell.Color56, tcell.Color57, tcell.Color58, tcell.Color59, tcell.Color60,
|
||||
tcell.Color61, tcell.Color62, tcell.Color63, tcell.Color64, tcell.Color65, tcell.Color66, tcell.Color67, tcell.Color68,
|
||||
tcell.Color69, tcell.Color70, tcell.Color71, tcell.Color72, tcell.Color73, tcell.Color74, tcell.Color75, tcell.Color76,
|
||||
tcell.Color77, tcell.Color78, tcell.Color79, tcell.Color80, tcell.Color81, tcell.Color82, tcell.Color83, tcell.Color84,
|
||||
tcell.Color85, tcell.Color86, tcell.Color87, tcell.Color88, tcell.Color89, tcell.Color90, tcell.Color91, tcell.Color92,
|
||||
tcell.Color93, tcell.Color94, tcell.Color95, tcell.Color96, tcell.Color97, tcell.Color98, tcell.Color99, tcell.Color100,
|
||||
tcell.Color101, tcell.Color102, tcell.Color103, tcell.Color104, tcell.Color105, tcell.Color106, tcell.Color107, tcell.Color108,
|
||||
tcell.Color109, tcell.Color110, tcell.Color111, tcell.Color112, tcell.Color113, tcell.Color114, tcell.Color115, tcell.Color116,
|
||||
tcell.Color117, tcell.Color118, tcell.Color119, tcell.Color120, tcell.Color121, tcell.Color122, tcell.Color123, tcell.Color124,
|
||||
tcell.Color125, tcell.Color126, tcell.Color127, tcell.Color128, tcell.Color129, tcell.Color130, tcell.Color131, tcell.Color132,
|
||||
tcell.Color133, tcell.Color134, tcell.Color135, tcell.Color136, tcell.Color137, tcell.Color138, tcell.Color139, tcell.Color140,
|
||||
tcell.Color141, tcell.Color142, tcell.Color143, tcell.Color144, tcell.Color145, tcell.Color146, tcell.Color147, tcell.Color148,
|
||||
tcell.Color149, tcell.Color150, tcell.Color151, tcell.Color152, tcell.Color153, tcell.Color154, tcell.Color155, tcell.Color156,
|
||||
tcell.Color157, tcell.Color158, tcell.Color159, tcell.Color160, tcell.Color161, tcell.Color162, tcell.Color163, tcell.Color164,
|
||||
tcell.Color165, tcell.Color166, tcell.Color167, tcell.Color168, tcell.Color169, tcell.Color170, tcell.Color171, tcell.Color172,
|
||||
tcell.Color173, tcell.Color174, tcell.Color175, tcell.Color176, tcell.Color177, tcell.Color178, tcell.Color179, tcell.Color180,
|
||||
tcell.Color181, tcell.Color182, tcell.Color183, tcell.Color184, tcell.Color185, tcell.Color186, tcell.Color187, tcell.Color188,
|
||||
tcell.Color189, tcell.Color190, tcell.Color191, tcell.Color192, tcell.Color193, tcell.Color194, tcell.Color195, tcell.Color196,
|
||||
tcell.Color197, tcell.Color198, tcell.Color199, tcell.Color200, tcell.Color201, tcell.Color202, tcell.Color203, tcell.Color204,
|
||||
tcell.Color205, tcell.Color206, tcell.Color207, tcell.Color208, tcell.Color209, tcell.Color210, tcell.Color211, tcell.Color212,
|
||||
tcell.Color213, tcell.Color214, tcell.Color215, tcell.Color216, tcell.Color217, tcell.Color218, tcell.Color219, tcell.Color220,
|
||||
tcell.Color221, tcell.Color222, tcell.Color223, tcell.Color224, tcell.Color225, tcell.Color226, tcell.Color227, tcell.Color228,
|
||||
tcell.Color229, tcell.Color230, tcell.Color231, tcell.Color232, tcell.Color233, tcell.Color234, tcell.Color235, tcell.Color236,
|
||||
tcell.Color237, tcell.Color238, tcell.Color239, tcell.Color240, tcell.Color241, tcell.Color242, tcell.Color243, tcell.Color244,
|
||||
tcell.Color245, tcell.Color246, tcell.Color247, tcell.Color248, tcell.Color249, tcell.Color250, tcell.Color251, tcell.Color252,
|
||||
tcell.Color253, tcell.Color254, tcell.Color255,
|
||||
if color == 0 {
|
||||
return tcell.ColorDefault
|
||||
}
|
||||
|
||||
if color >= 0 && color < len(colors) {
|
||||
return colors[color]
|
||||
}
|
||||
|
||||
return tcell.ColorDefault
|
||||
return tcell.PaletteColor(color)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,12 @@ const (
|
||||
DoubleClickThreshold = 400 // How many milliseconds to wait before a second click is not a double click
|
||||
)
|
||||
|
||||
var Bindings map[string]string
|
||||
var Bindings map[string]map[string]string
|
||||
|
||||
func init() {
|
||||
Bindings = make(map[string]string)
|
||||
Bindings = map[string]map[string]string{
|
||||
"command": make(map[string]string),
|
||||
"buffer": make(map[string]string),
|
||||
"terminal": make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,9 +479,7 @@ func (pl PluginPackages) GetAllVersions(name string) PluginVersions {
|
||||
result := make(PluginVersions, 0)
|
||||
p := pl.Get(name)
|
||||
if p != nil {
|
||||
for _, v := range p.Versions {
|
||||
result = append(result, v)
|
||||
}
|
||||
result = append(result, p.Versions...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -104,7 +104,7 @@ func InitGlobalSettings() error {
|
||||
for k, v := range parsedSettings {
|
||||
if !strings.HasPrefix(reflect.TypeOf(v).String(), "map") {
|
||||
if _, ok := GlobalSettings[k]; ok && !verifySetting(k, reflect.TypeOf(v), reflect.TypeOf(GlobalSettings[k])) {
|
||||
err = errors.New(fmt.Sprintf("Global Error: setting '%s' has incorrect type (%s), using default value: %v (%s)", k, reflect.TypeOf(v), GlobalSettings[k], reflect.TypeOf(GlobalSettings[k])))
|
||||
err = fmt.Errorf("Global Error: setting '%s' has incorrect type (%s), using default value: %v (%s)", k, reflect.TypeOf(v), GlobalSettings[k], reflect.TypeOf(GlobalSettings[k]))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ func InitLocalSettings(settings map[string]interface{}, path string) error {
|
||||
if settings["filetype"].(string) == k[3:] {
|
||||
for k1, v1 := range v.(map[string]interface{}) {
|
||||
if _, ok := settings[k1]; ok && !verifySetting(k1, reflect.TypeOf(v1), reflect.TypeOf(settings[k1])) {
|
||||
parseError = errors.New(fmt.Sprintf("Error: setting '%s' has incorrect type (%s), using default value: %v (%s)", k, reflect.TypeOf(v1), settings[k1], reflect.TypeOf(settings[k1])))
|
||||
parseError = fmt.Errorf("Error: setting '%s' has incorrect type (%s), using default value: %v (%s)", k, reflect.TypeOf(v1), settings[k1], reflect.TypeOf(settings[k1]))
|
||||
continue
|
||||
}
|
||||
settings[k1] = v1
|
||||
@@ -141,7 +141,7 @@ func InitLocalSettings(settings map[string]interface{}, path string) error {
|
||||
if g.MatchString(path) {
|
||||
for k1, v1 := range v.(map[string]interface{}) {
|
||||
if _, ok := settings[k1]; ok && !verifySetting(k1, reflect.TypeOf(v1), reflect.TypeOf(settings[k1])) {
|
||||
parseError = errors.New(fmt.Sprintf("Error: setting '%s' has incorrect type (%s), using default value: %v (%s)", k, reflect.TypeOf(v1), settings[k1], reflect.TypeOf(settings[k1])))
|
||||
parseError = fmt.Errorf("Error: setting '%s' has incorrect type (%s), using default value: %v (%s)", k, reflect.TypeOf(v1), settings[k1], reflect.TypeOf(settings[k1]))
|
||||
continue
|
||||
}
|
||||
settings[k1] = v1
|
||||
@@ -269,7 +269,8 @@ var defaultCommonSettings = map[string]interface{}{
|
||||
"fastdirty": false,
|
||||
"fileformat": "unix",
|
||||
"filetype": "unknown",
|
||||
"ignorecase": false,
|
||||
"incsearch": true,
|
||||
"ignorecase": true,
|
||||
"indentchar": " ",
|
||||
"keepautoindent": false,
|
||||
"matchbrace": true,
|
||||
@@ -296,6 +297,7 @@ var defaultCommonSettings = map[string]interface{}{
|
||||
"tabsize": float64(4),
|
||||
"tabstospaces": false,
|
||||
"useprimary": true,
|
||||
"wordwrap": false,
|
||||
}
|
||||
|
||||
func GetInfoBarOffset() int {
|
||||
|
||||
@@ -23,15 +23,20 @@ type BufWindow struct {
|
||||
|
||||
sline *StatusLine
|
||||
|
||||
gutterOffset int
|
||||
drawStatus bool
|
||||
bufWidth int
|
||||
bufHeight int
|
||||
gutterOffset int
|
||||
hasMessage bool
|
||||
maxLineNumLength int
|
||||
drawDivider bool
|
||||
}
|
||||
|
||||
// NewBufWindow creates a new window at a location in the screen with a width and height
|
||||
func NewBufWindow(x, y, width, height int, buf *buffer.Buffer) *BufWindow {
|
||||
w := new(BufWindow)
|
||||
w.View = new(View)
|
||||
w.X, w.Y, w.Width, w.Height, w.Buf = x, y, width, height, buf
|
||||
w.X, w.Y, w.Width, w.Height = x, y, width, height
|
||||
w.SetBuffer(buf)
|
||||
w.active = true
|
||||
|
||||
w.sline = NewStatusLine(w)
|
||||
@@ -41,6 +46,23 @@ func NewBufWindow(x, y, width, height int, buf *buffer.Buffer) *BufWindow {
|
||||
|
||||
func (w *BufWindow) SetBuffer(b *buffer.Buffer) {
|
||||
w.Buf = b
|
||||
b.OptionCallback = func(option string, nativeValue interface{}) {
|
||||
if option == "softwrap" {
|
||||
if nativeValue.(bool) {
|
||||
w.StartCol = 0
|
||||
} else {
|
||||
w.StartLine.Row = 0
|
||||
}
|
||||
w.Relocate()
|
||||
|
||||
for _, c := range w.Buf.GetCursors() {
|
||||
c.LastVisualX = c.GetVisualX()
|
||||
}
|
||||
}
|
||||
}
|
||||
b.GetVisualX = func(loc buffer.Loc) int {
|
||||
return w.VLocFromLoc(loc).VisualX
|
||||
}
|
||||
}
|
||||
|
||||
func (w *BufWindow) GetView() *View {
|
||||
@@ -53,7 +75,15 @@ func (w *BufWindow) SetView(view *View) {
|
||||
|
||||
func (w *BufWindow) Resize(width, height int) {
|
||||
w.Width, w.Height = width, height
|
||||
w.updateDisplayInfo()
|
||||
|
||||
w.Relocate()
|
||||
|
||||
if w.Buf.Settings["softwrap"].(bool) {
|
||||
for _, c := range w.Buf.GetCursors() {
|
||||
c.LastVisualX = c.GetVisualX()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *BufWindow) SetActive(b bool) {
|
||||
@@ -64,6 +94,63 @@ func (w *BufWindow) IsActive() bool {
|
||||
return w.active
|
||||
}
|
||||
|
||||
// BufView returns the width, height and x,y location of the actual buffer.
|
||||
// It is not exactly the same as the whole window which also contains gutter,
|
||||
// ruler, scrollbar and statusline.
|
||||
func (w *BufWindow) BufView() View {
|
||||
return View{
|
||||
X: w.X + w.gutterOffset,
|
||||
Y: w.Y,
|
||||
Width: w.bufWidth,
|
||||
Height: w.bufHeight,
|
||||
StartLine: w.StartLine,
|
||||
StartCol: w.StartCol,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *BufWindow) updateDisplayInfo() {
|
||||
b := w.Buf
|
||||
|
||||
w.drawDivider = false
|
||||
if !b.Settings["statusline"].(bool) {
|
||||
_, h := screen.Screen.Size()
|
||||
infoY := h
|
||||
if config.GetGlobalOption("infobar").(bool) {
|
||||
infoY--
|
||||
}
|
||||
if w.Y+w.Height != infoY {
|
||||
w.drawDivider = true
|
||||
}
|
||||
}
|
||||
|
||||
w.bufHeight = w.Height
|
||||
if b.Settings["statusline"].(bool) || w.drawDivider {
|
||||
w.bufHeight--
|
||||
}
|
||||
|
||||
w.hasMessage = len(b.Messages) > 0
|
||||
|
||||
// We need to know the string length of the largest line number
|
||||
// so we can pad appropriately when displaying line numbers
|
||||
w.maxLineNumLength = len(strconv.Itoa(b.LinesNum()))
|
||||
|
||||
w.gutterOffset = 0
|
||||
if w.hasMessage {
|
||||
w.gutterOffset += 2
|
||||
}
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
w.gutterOffset++
|
||||
}
|
||||
if b.Settings["ruler"].(bool) {
|
||||
w.gutterOffset += w.maxLineNumLength + 1
|
||||
}
|
||||
|
||||
w.bufWidth = w.Width - w.gutterOffset
|
||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
||||
w.bufWidth--
|
||||
}
|
||||
}
|
||||
|
||||
func (w *BufWindow) getStartInfo(n, lineN int) ([]byte, int, int, *tcell.Style) {
|
||||
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
|
||||
width := 0
|
||||
@@ -106,63 +193,49 @@ func (w *BufWindow) Clear() {
|
||||
}
|
||||
}
|
||||
|
||||
// Bottomline returns the line number of the lowest line in the view
|
||||
// You might think that this is obviously just v.StartLine + v.Height
|
||||
// but if softwrap is enabled things get complicated since one buffer
|
||||
// line can take up multiple lines in the view
|
||||
func (w *BufWindow) Bottomline() int {
|
||||
if !w.Buf.Settings["softwrap"].(bool) {
|
||||
h := w.StartLine + w.Height - 1
|
||||
if w.drawStatus {
|
||||
h--
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
l := w.LocFromVisual(buffer.Loc{0, w.Y + w.Height})
|
||||
|
||||
return l.Y
|
||||
}
|
||||
|
||||
// Relocate moves the view window so that the cursor is in view
|
||||
// This is useful if the user has scrolled far away, and then starts typing
|
||||
// Returns true if the window location is moved
|
||||
func (w *BufWindow) Relocate() bool {
|
||||
b := w.Buf
|
||||
// how many buffer lines are in the view
|
||||
height := w.Bottomline() + 1 - w.StartLine
|
||||
h := w.Height
|
||||
if w.drawStatus {
|
||||
h--
|
||||
}
|
||||
height := w.bufHeight
|
||||
ret := false
|
||||
activeC := w.Buf.GetActiveCursor()
|
||||
cy := activeC.Y
|
||||
scrollmargin := int(b.Settings["scrollmargin"].(float64))
|
||||
if cy < w.StartLine+scrollmargin && cy > scrollmargin-1 {
|
||||
w.StartLine = cy - scrollmargin
|
||||
|
||||
c := w.SLocFromLoc(activeC.Loc)
|
||||
bStart := SLoc{0, 0}
|
||||
bEnd := w.SLocFromLoc(b.End())
|
||||
|
||||
if c.LessThan(w.Scroll(w.StartLine, scrollmargin)) && c.GreaterThan(w.Scroll(bStart, scrollmargin-1)) {
|
||||
w.StartLine = w.Scroll(c, -scrollmargin)
|
||||
ret = true
|
||||
} else if cy < w.StartLine {
|
||||
w.StartLine = cy
|
||||
} else if c.LessThan(w.StartLine) {
|
||||
w.StartLine = c
|
||||
ret = true
|
||||
}
|
||||
if cy > w.StartLine+height-1-scrollmargin && cy < b.LinesNum()-scrollmargin {
|
||||
w.StartLine = cy - height + 1 + scrollmargin
|
||||
if c.GreaterThan(w.Scroll(w.StartLine, height-1-scrollmargin)) && c.LessThan(w.Scroll(bEnd, -scrollmargin+1)) {
|
||||
w.StartLine = w.Scroll(c, -height+1+scrollmargin)
|
||||
ret = true
|
||||
} else if cy >= b.LinesNum()-scrollmargin && cy >= height {
|
||||
w.StartLine = b.LinesNum() - height
|
||||
} else if c.GreaterThan(w.Scroll(bEnd, -scrollmargin)) && c.GreaterThan(w.Scroll(w.StartLine, height-1)) {
|
||||
w.StartLine = w.Scroll(bEnd, -height+1)
|
||||
ret = true
|
||||
}
|
||||
|
||||
// horizontal relocation (scrolling)
|
||||
if !b.Settings["softwrap"].(bool) {
|
||||
cx := activeC.GetVisualX()
|
||||
rw := runewidth.RuneWidth(activeC.RuneUnder(activeC.X))
|
||||
if rw == 0 {
|
||||
rw = 1 // tab or newline
|
||||
}
|
||||
|
||||
if cx < w.StartCol {
|
||||
w.StartCol = cx
|
||||
ret = true
|
||||
}
|
||||
if cx+w.gutterOffset+1 > w.StartCol+w.Width {
|
||||
w.StartCol = cx - w.Width + w.gutterOffset + 1
|
||||
if cx+w.gutterOffset+rw > w.StartCol+w.Width {
|
||||
w.StartCol = cx - w.Width + w.gutterOffset + rw
|
||||
ret = true
|
||||
}
|
||||
}
|
||||
@@ -171,123 +244,18 @@ func (w *BufWindow) Relocate() bool {
|
||||
|
||||
// LocFromVisual takes a visual location (x and y position) and returns the
|
||||
// position in the buffer corresponding to the visual location
|
||||
// Computing the buffer location requires essentially drawing the entire screen
|
||||
// to account for complications like softwrap, wide characters, and horizontal scrolling
|
||||
// If the requested position does not correspond to a buffer location it returns
|
||||
// the nearest position
|
||||
func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc {
|
||||
b := w.Buf
|
||||
|
||||
hasMessage := len(b.Messages) > 0
|
||||
bufHeight := w.Height
|
||||
if w.drawStatus {
|
||||
bufHeight--
|
||||
vx := svloc.X - w.X - w.gutterOffset
|
||||
if vx < 0 {
|
||||
vx = 0
|
||||
}
|
||||
|
||||
bufWidth := w.Width
|
||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
||||
bufWidth--
|
||||
vloc := VLoc{
|
||||
SLoc: w.Scroll(w.StartLine, svloc.Y-w.Y),
|
||||
VisualX: vx + w.StartCol,
|
||||
}
|
||||
|
||||
// We need to know the string length of the largest line number
|
||||
// so we can pad appropriately when displaying line numbers
|
||||
maxLineNumLength := len(strconv.Itoa(b.LinesNum()))
|
||||
|
||||
tabsize := int(b.Settings["tabsize"].(float64))
|
||||
softwrap := b.Settings["softwrap"].(bool)
|
||||
|
||||
// this represents the current draw position
|
||||
// within the current window
|
||||
vloc := buffer.Loc{X: 0, Y: 0}
|
||||
|
||||
// this represents the current draw position in the buffer (char positions)
|
||||
bloc := buffer.Loc{X: -1, Y: w.StartLine}
|
||||
|
||||
for vloc.Y = 0; vloc.Y < bufHeight; vloc.Y++ {
|
||||
vloc.X = 0
|
||||
if hasMessage {
|
||||
vloc.X += 2
|
||||
}
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
vloc.X++
|
||||
}
|
||||
if b.Settings["ruler"].(bool) {
|
||||
vloc.X += maxLineNumLength + 1
|
||||
}
|
||||
|
||||
line := b.LineBytes(bloc.Y)
|
||||
line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, w.StartCol, tabsize)
|
||||
bloc.X = bslice
|
||||
|
||||
draw := func() {
|
||||
if nColsBeforeStart <= 0 {
|
||||
vloc.X++
|
||||
}
|
||||
nColsBeforeStart--
|
||||
}
|
||||
|
||||
totalwidth := w.StartCol - nColsBeforeStart
|
||||
|
||||
if svloc.X <= vloc.X+w.X && vloc.Y+w.Y == svloc.Y {
|
||||
return bloc
|
||||
}
|
||||
for len(line) > 0 {
|
||||
if vloc.X+w.X == svloc.X && vloc.Y+w.Y == svloc.Y {
|
||||
return bloc
|
||||
}
|
||||
|
||||
r, _, size := util.DecodeCharacter(line)
|
||||
draw()
|
||||
width := 0
|
||||
|
||||
switch r {
|
||||
case '\t':
|
||||
ts := tabsize - (totalwidth % tabsize)
|
||||
width = ts
|
||||
default:
|
||||
width = runewidth.RuneWidth(r)
|
||||
}
|
||||
|
||||
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
|
||||
if width > 1 {
|
||||
for i := 1; i < width; i++ {
|
||||
if vloc.X+w.X == svloc.X && vloc.Y+w.Y == svloc.Y {
|
||||
return bloc
|
||||
}
|
||||
draw()
|
||||
}
|
||||
}
|
||||
bloc.X++
|
||||
line = line[size:]
|
||||
|
||||
totalwidth += width
|
||||
|
||||
// If we reach the end of the window then we either stop or we wrap for softwrap
|
||||
if vloc.X >= bufWidth {
|
||||
if !softwrap {
|
||||
break
|
||||
} else {
|
||||
vloc.Y++
|
||||
if vloc.Y >= bufHeight {
|
||||
break
|
||||
}
|
||||
vloc.X = w.gutterOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
if vloc.Y+w.Y == svloc.Y {
|
||||
return bloc
|
||||
}
|
||||
|
||||
if bloc.Y+1 >= b.LinesNum() || vloc.Y+1 >= bufHeight {
|
||||
return bloc
|
||||
}
|
||||
|
||||
bloc.X = w.StartCol
|
||||
bloc.Y++
|
||||
}
|
||||
|
||||
return buffer.Loc{}
|
||||
return w.LocFromVLoc(vloc)
|
||||
}
|
||||
|
||||
func (w *BufWindow) drawGutter(vloc *buffer.Loc, bloc *buffer.Loc) {
|
||||
@@ -334,7 +302,7 @@ func (w *BufWindow) drawDiffGutter(backgroundStyle tcell.Style, softwrapped bool
|
||||
vloc.X++
|
||||
}
|
||||
|
||||
func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxLineNumLength int, vloc *buffer.Loc, bloc *buffer.Loc) {
|
||||
func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, vloc *buffer.Loc, bloc *buffer.Loc) {
|
||||
cursorLine := w.Buf.GetActiveCursor().Loc.Y
|
||||
var lineInt int
|
||||
if w.Buf.Settings["relativeruler"] == false || cursorLine == bloc.Y {
|
||||
@@ -345,7 +313,7 @@ func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxL
|
||||
lineNum := strconv.Itoa(util.Abs(lineInt))
|
||||
|
||||
// Write the spaces before the line number if necessary
|
||||
for i := 0; i < maxLineNumLength-len(lineNum); i++ {
|
||||
for i := 0; i < w.maxLineNumLength-len(lineNum); i++ {
|
||||
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
||||
vloc.X++
|
||||
}
|
||||
@@ -392,16 +360,7 @@ func (w *BufWindow) displayBuffer() {
|
||||
return
|
||||
}
|
||||
|
||||
hasMessage := len(b.Messages) > 0
|
||||
bufHeight := w.Height
|
||||
if w.drawStatus {
|
||||
bufHeight--
|
||||
}
|
||||
|
||||
bufWidth := w.Width
|
||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
||||
bufWidth--
|
||||
}
|
||||
maxWidth := w.gutterOffset + w.bufWidth
|
||||
|
||||
if b.ModifiedThisFrame {
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
@@ -462,25 +421,27 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
}
|
||||
|
||||
// We need to know the string length of the largest line number
|
||||
// so we can pad appropriately when displaying line numbers
|
||||
maxLineNumLength := len(strconv.Itoa(b.LinesNum()))
|
||||
|
||||
softwrap := b.Settings["softwrap"].(bool)
|
||||
wordwrap := softwrap && b.Settings["wordwrap"].(bool)
|
||||
|
||||
tabsize := util.IntOpt(b.Settings["tabsize"])
|
||||
colorcolumn := util.IntOpt(b.Settings["colorcolumn"])
|
||||
|
||||
// this represents the current draw position
|
||||
// within the current window
|
||||
vloc := buffer.Loc{X: 0, Y: 0}
|
||||
if softwrap {
|
||||
// the start line may be partially out of the current window
|
||||
vloc.Y = -w.StartLine.Row
|
||||
}
|
||||
|
||||
// this represents the current draw position in the buffer (char positions)
|
||||
bloc := buffer.Loc{X: -1, Y: w.StartLine}
|
||||
bloc := buffer.Loc{X: -1, Y: w.StartLine.Line}
|
||||
|
||||
cursors := b.GetCursors()
|
||||
|
||||
curStyle := config.DefStyle
|
||||
for vloc.Y = 0; vloc.Y < bufHeight; vloc.Y++ {
|
||||
for ; vloc.Y < w.bufHeight; vloc.Y++ {
|
||||
vloc.X = 0
|
||||
|
||||
currentLine := false
|
||||
@@ -496,88 +457,92 @@ func (w *BufWindow) displayBuffer() {
|
||||
s = curNumStyle
|
||||
}
|
||||
|
||||
if hasMessage {
|
||||
w.drawGutter(&vloc, &bloc)
|
||||
}
|
||||
if vloc.Y >= 0 {
|
||||
if w.hasMessage {
|
||||
w.drawGutter(&vloc, &bloc)
|
||||
}
|
||||
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
w.drawDiffGutter(s, false, &vloc, &bloc)
|
||||
}
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
w.drawDiffGutter(s, false, &vloc, &bloc)
|
||||
}
|
||||
|
||||
if b.Settings["ruler"].(bool) {
|
||||
w.drawLineNum(s, false, maxLineNumLength, &vloc, &bloc)
|
||||
if b.Settings["ruler"].(bool) {
|
||||
w.drawLineNum(s, false, &vloc, &bloc)
|
||||
}
|
||||
} else {
|
||||
vloc.X = w.gutterOffset
|
||||
}
|
||||
|
||||
w.gutterOffset = vloc.X
|
||||
|
||||
line, nColsBeforeStart, bslice, startStyle := w.getStartInfo(w.StartCol, bloc.Y)
|
||||
if startStyle != nil {
|
||||
curStyle = *startStyle
|
||||
}
|
||||
bloc.X = bslice
|
||||
|
||||
draw := func(r rune, combc []rune, style tcell.Style, showcursor bool) {
|
||||
if nColsBeforeStart <= 0 {
|
||||
_, origBg, _ := style.Decompose()
|
||||
_, defBg, _ := config.DefStyle.Decompose()
|
||||
draw := func(r rune, combc []rune, style tcell.Style, highlight bool, showcursor bool) {
|
||||
if nColsBeforeStart <= 0 && vloc.Y >= 0 {
|
||||
if highlight {
|
||||
_, origBg, _ := style.Decompose()
|
||||
_, defBg, _ := config.DefStyle.Decompose()
|
||||
|
||||
// syntax highlighting with non-default background takes precedence
|
||||
// over cursor-line and color-column
|
||||
dontOverrideBackground := origBg != defBg
|
||||
// syntax highlighting with non-default background takes precedence
|
||||
// over cursor-line and color-column
|
||||
dontOverrideBackground := origBg != defBg
|
||||
|
||||
for _, c := range cursors {
|
||||
if c.HasSelection() &&
|
||||
(bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) ||
|
||||
bloc.LessThan(c.CurSelection[0]) && bloc.GreaterEqual(c.CurSelection[1])) {
|
||||
// The current character is selected
|
||||
style = config.DefStyle.Reverse(true)
|
||||
for _, c := range cursors {
|
||||
if c.HasSelection() &&
|
||||
(bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) ||
|
||||
bloc.LessThan(c.CurSelection[0]) && bloc.GreaterEqual(c.CurSelection[1])) {
|
||||
// The current character is selected
|
||||
style = config.DefStyle.Reverse(true)
|
||||
|
||||
if s, ok := config.Colorscheme["selection"]; ok {
|
||||
style = s
|
||||
if s, ok := config.Colorscheme["selection"]; ok {
|
||||
style = s
|
||||
}
|
||||
}
|
||||
|
||||
if b.Settings["cursorline"].(bool) && w.active && !dontOverrideBackground &&
|
||||
!c.HasSelection() && c.Y == bloc.Y {
|
||||
if s, ok := config.Colorscheme["cursor-line"]; ok {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Background(fg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if b.Settings["cursorline"].(bool) && w.active && !dontOverrideBackground &&
|
||||
!c.HasSelection() && c.Y == bloc.Y {
|
||||
if s, ok := config.Colorscheme["cursor-line"]; ok {
|
||||
for _, m := range b.Messages {
|
||||
if bloc.GreaterEqual(m.Start) && bloc.LessThan(m.End) ||
|
||||
bloc.LessThan(m.End) && bloc.GreaterEqual(m.Start) {
|
||||
style = style.Underline(true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if r == '\t' {
|
||||
indentrunes := []rune(b.Settings["indentchar"].(string))
|
||||
// if empty indentchar settings, use space
|
||||
if len(indentrunes) == 0 {
|
||||
indentrunes = []rune{' '}
|
||||
}
|
||||
|
||||
r = indentrunes[0]
|
||||
if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Foreground(fg)
|
||||
}
|
||||
}
|
||||
|
||||
if s, ok := config.Colorscheme["color-column"]; ok {
|
||||
if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !dontOverrideBackground {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Background(fg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range b.Messages {
|
||||
if bloc.GreaterEqual(m.Start) && bloc.LessThan(m.End) ||
|
||||
bloc.LessThan(m.End) && bloc.GreaterEqual(m.Start) {
|
||||
style = style.Underline(true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if r == '\t' {
|
||||
indentrunes := []rune(b.Settings["indentchar"].(string))
|
||||
// if empty indentchar settings, use space
|
||||
if indentrunes == nil || len(indentrunes) == 0 {
|
||||
indentrunes = []rune{' '}
|
||||
}
|
||||
|
||||
r = indentrunes[0]
|
||||
if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Foreground(fg)
|
||||
}
|
||||
}
|
||||
|
||||
if s, ok := config.Colorscheme["color-column"]; ok {
|
||||
if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !dontOverrideBackground {
|
||||
fg, _, _ := s.Decompose()
|
||||
style = style.Background(fg)
|
||||
}
|
||||
}
|
||||
|
||||
for _, mb := range matchingBraces {
|
||||
if mb.X == bloc.X && mb.Y == bloc.Y {
|
||||
style = style.Underline(true)
|
||||
for _, mb := range matchingBraces {
|
||||
if mb.X == bloc.X && mb.Y == bloc.Y {
|
||||
style = style.Underline(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,64 +555,123 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if nColsBeforeStart <= 0 {
|
||||
vloc.X++
|
||||
}
|
||||
nColsBeforeStart--
|
||||
}
|
||||
|
||||
wrap := func() {
|
||||
vloc.X = 0
|
||||
if w.hasMessage {
|
||||
w.drawGutter(&vloc, &bloc)
|
||||
}
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
w.drawDiffGutter(lineNumStyle, true, &vloc, &bloc)
|
||||
}
|
||||
|
||||
// This will draw an empty line number because the current line is wrapped
|
||||
if b.Settings["ruler"].(bool) {
|
||||
w.drawLineNum(lineNumStyle, true, &vloc, &bloc)
|
||||
}
|
||||
}
|
||||
|
||||
type glyph struct {
|
||||
r rune
|
||||
combc []rune
|
||||
style tcell.Style
|
||||
width int
|
||||
}
|
||||
|
||||
var word []glyph
|
||||
if wordwrap {
|
||||
word = make([]glyph, 0, w.bufWidth)
|
||||
} else {
|
||||
word = make([]glyph, 0, 1)
|
||||
}
|
||||
wordwidth := 0
|
||||
|
||||
totalwidth := w.StartCol - nColsBeforeStart
|
||||
for len(line) > 0 {
|
||||
r, combc, size := util.DecodeCharacter(line)
|
||||
line = line[size:]
|
||||
|
||||
curStyle, _ = w.getStyle(curStyle, bloc)
|
||||
|
||||
draw(r, combc, curStyle, true)
|
||||
loc := buffer.Loc{X: bloc.X + len(word), Y: bloc.Y}
|
||||
curStyle, _ = w.getStyle(curStyle, loc)
|
||||
|
||||
width := 0
|
||||
|
||||
char := ' '
|
||||
switch r {
|
||||
case '\t':
|
||||
ts := tabsize - (totalwidth % tabsize)
|
||||
width = ts
|
||||
width = util.Min(ts, maxWidth-vloc.X)
|
||||
totalwidth += ts
|
||||
default:
|
||||
width = runewidth.RuneWidth(r)
|
||||
char = '@'
|
||||
totalwidth += width
|
||||
}
|
||||
|
||||
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
|
||||
if width > 1 {
|
||||
for i := 1; i < width; i++ {
|
||||
draw(char, nil, curStyle, false)
|
||||
word = append(word, glyph{r, combc, curStyle, width})
|
||||
wordwidth += width
|
||||
|
||||
// Collect a complete word to know its width.
|
||||
// If wordwrap is off, every single character is a complete "word".
|
||||
if wordwrap {
|
||||
if !util.IsWhitespace(r) && len(line) > 0 && wordwidth < w.bufWidth {
|
||||
continue
|
||||
}
|
||||
}
|
||||
bloc.X++
|
||||
line = line[size:]
|
||||
|
||||
totalwidth += width
|
||||
// If a word (or just a wide rune) does not fit in the window
|
||||
if vloc.X+wordwidth > maxWidth && vloc.X > w.gutterOffset {
|
||||
for vloc.X < maxWidth {
|
||||
draw(' ', nil, config.DefStyle, false, false)
|
||||
}
|
||||
|
||||
// If we reach the end of the window then we either stop or we wrap for softwrap
|
||||
if vloc.X >= bufWidth {
|
||||
// We either stop or we wrap to draw the word in the next line
|
||||
if !softwrap {
|
||||
break
|
||||
} else {
|
||||
vloc.Y++
|
||||
if vloc.Y >= bufHeight {
|
||||
if vloc.Y >= w.bufHeight {
|
||||
break
|
||||
}
|
||||
vloc.X = 0
|
||||
if hasMessage {
|
||||
w.drawGutter(&vloc, &bloc)
|
||||
}
|
||||
if b.Settings["diffgutter"].(bool) {
|
||||
w.drawDiffGutter(lineNumStyle, true, &vloc, &bloc)
|
||||
wrap()
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range word {
|
||||
draw(r.r, r.combc, r.style, true, true)
|
||||
|
||||
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
|
||||
if r.width > 1 {
|
||||
char := ' '
|
||||
if r.r != '\t' {
|
||||
char = '@'
|
||||
}
|
||||
|
||||
// This will draw an empty line number because the current line is wrapped
|
||||
if b.Settings["ruler"].(bool) {
|
||||
w.drawLineNum(lineNumStyle, true, maxLineNumLength, &vloc, &bloc)
|
||||
for i := 1; i < r.width; i++ {
|
||||
draw(char, nil, r.style, true, false)
|
||||
}
|
||||
}
|
||||
bloc.X++
|
||||
}
|
||||
|
||||
word = word[:0]
|
||||
wordwidth = 0
|
||||
|
||||
// If we reach the end of the window then we either stop or we wrap for softwrap
|
||||
if vloc.X >= maxWidth {
|
||||
if !softwrap {
|
||||
break
|
||||
} else {
|
||||
vloc.Y++
|
||||
if vloc.Y >= w.bufHeight {
|
||||
break
|
||||
}
|
||||
wrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,7 +685,7 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := vloc.X; i < bufWidth; i++ {
|
||||
for i := vloc.X; i < maxWidth; i++ {
|
||||
curStyle := style
|
||||
if s, ok := config.Colorscheme["color-column"]; ok {
|
||||
if colorcolumn != 0 && i-w.gutterOffset+w.StartCol == colorcolumn {
|
||||
@@ -672,9 +696,9 @@ func (w *BufWindow) displayBuffer() {
|
||||
screen.SetContent(i+w.X, vloc.Y+w.Y, ' ', nil, curStyle)
|
||||
}
|
||||
|
||||
if vloc.X != bufWidth {
|
||||
if vloc.X != maxWidth {
|
||||
// Display newline within a selection
|
||||
draw(' ', nil, config.DefStyle, true)
|
||||
draw(' ', nil, config.DefStyle, true, true)
|
||||
}
|
||||
|
||||
bloc.X = w.StartCol
|
||||
@@ -686,18 +710,9 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
|
||||
func (w *BufWindow) displayStatusLine() {
|
||||
_, h := screen.Screen.Size()
|
||||
infoY := h
|
||||
if config.GetGlobalOption("infobar").(bool) {
|
||||
infoY--
|
||||
}
|
||||
|
||||
if w.Buf.Settings["statusline"].(bool) {
|
||||
w.drawStatus = true
|
||||
w.sline.Display()
|
||||
} else if w.Y+w.Height != infoY {
|
||||
w.drawStatus = true
|
||||
|
||||
} else if w.drawDivider {
|
||||
divchars := config.GetGlobalOption("divchars").(string)
|
||||
if util.CharacterCountInString(divchars) != 2 {
|
||||
divchars = "|-"
|
||||
@@ -719,30 +734,24 @@ func (w *BufWindow) displayStatusLine() {
|
||||
for x := w.X; x < w.X+w.Width; x++ {
|
||||
screen.SetContent(x, w.Y+w.Height-1, divchar, combc, dividerStyle)
|
||||
}
|
||||
} else {
|
||||
w.drawStatus = false
|
||||
}
|
||||
}
|
||||
|
||||
func (w *BufWindow) displayScrollBar() {
|
||||
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
||||
scrollX := w.X + w.Width - 1
|
||||
bufHeight := w.Height
|
||||
if w.drawStatus {
|
||||
bufHeight--
|
||||
}
|
||||
barsize := int(float64(w.Height) / float64(w.Buf.LinesNum()) * float64(w.Height))
|
||||
if barsize < 1 {
|
||||
barsize = 1
|
||||
}
|
||||
barstart := w.Y + int(float64(w.StartLine)/float64(w.Buf.LinesNum())*float64(w.Height))
|
||||
barstart := w.Y + int(float64(w.StartLine.Line)/float64(w.Buf.LinesNum())*float64(w.Height))
|
||||
|
||||
scrollBarStyle := config.DefStyle.Reverse(true)
|
||||
if style, ok := config.Colorscheme["scrollbar"]; ok {
|
||||
scrollBarStyle = style
|
||||
}
|
||||
|
||||
for y := barstart; y < util.Min(barstart+barsize, w.Y+bufHeight); y++ {
|
||||
for y := barstart; y < util.Min(barstart+barsize, w.Y+w.bufHeight); y++ {
|
||||
screen.SetContent(scrollX, y, '|', nil, scrollBarStyle)
|
||||
}
|
||||
}
|
||||
@@ -750,6 +759,8 @@ func (w *BufWindow) displayScrollBar() {
|
||||
|
||||
// Display displays the buffer and the statusline
|
||||
func (w *BufWindow) Display() {
|
||||
w.updateDisplayInfo()
|
||||
|
||||
w.displayStatusLine()
|
||||
w.displayScrollBar()
|
||||
w.displayBuffer()
|
||||
|
||||
@@ -72,6 +72,23 @@ func (i *InfoWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc {
|
||||
return buffer.Loc{c.GetCharPosInLine(l, vloc.X-n), 0}
|
||||
}
|
||||
|
||||
func (i *InfoWindow) BufView() View {
|
||||
return View{
|
||||
X: 0,
|
||||
Y: i.Y,
|
||||
Width: i.Width,
|
||||
Height: 1,
|
||||
StartLine: SLoc{0, 0},
|
||||
StartCol: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *InfoWindow) Scroll(s SLoc, n int) SLoc { return s }
|
||||
func (i *InfoWindow) Diff(s1, s2 SLoc) int { return 0 }
|
||||
func (i *InfoWindow) SLocFromLoc(loc buffer.Loc) SLoc { return SLoc{0, 0} }
|
||||
func (i *InfoWindow) VLocFromLoc(loc buffer.Loc) VLoc { return VLoc{SLoc{0, 0}, loc.X} }
|
||||
func (i *InfoWindow) LocFromVLoc(vloc VLoc) buffer.Loc { return buffer.Loc{vloc.VisualX, 0} }
|
||||
|
||||
func (i *InfoWindow) Clear() {
|
||||
for x := 0; x < i.Width; x++ {
|
||||
screen.SetContent(x, i.Y, ' ', nil, i.defStyle())
|
||||
|
||||
326
internal/display/softwrap.go
Normal file
326
internal/display/softwrap.go
Normal file
@@ -0,0 +1,326 @@
|
||||
package display
|
||||
|
||||
import (
|
||||
runewidth "github.com/mattn/go-runewidth"
|
||||
"github.com/zyedidia/micro/v2/internal/buffer"
|
||||
"github.com/zyedidia/micro/v2/internal/util"
|
||||
)
|
||||
|
||||
// SLoc represents a vertical scrolling location, i.e. a location of a visual line
|
||||
// in the buffer. When softwrap is enabled, a buffer line may be displayed as
|
||||
// multiple visual lines (rows). So SLoc stores a number of a line in the buffer
|
||||
// and a number of a row within this line.
|
||||
type SLoc struct {
|
||||
Line, Row int
|
||||
}
|
||||
|
||||
// LessThan returns true if s is less b
|
||||
func (s SLoc) LessThan(b SLoc) bool {
|
||||
if s.Line < b.Line {
|
||||
return true
|
||||
}
|
||||
return s.Line == b.Line && s.Row < b.Row
|
||||
}
|
||||
|
||||
// GreaterThan returns true if s is bigger than b
|
||||
func (s SLoc) GreaterThan(b SLoc) bool {
|
||||
if s.Line > b.Line {
|
||||
return true
|
||||
}
|
||||
return s.Line == b.Line && s.Row > b.Row
|
||||
}
|
||||
|
||||
// VLoc represents a location in the buffer as a visual location in the
|
||||
// linewrapped buffer.
|
||||
type VLoc struct {
|
||||
SLoc
|
||||
VisualX int
|
||||
}
|
||||
|
||||
type SoftWrap interface {
|
||||
Scroll(s SLoc, n int) SLoc
|
||||
Diff(s1, s2 SLoc) int
|
||||
SLocFromLoc(loc buffer.Loc) SLoc
|
||||
VLocFromLoc(loc buffer.Loc) VLoc
|
||||
LocFromVLoc(vloc VLoc) buffer.Loc
|
||||
}
|
||||
|
||||
func (w *BufWindow) getVLocFromLoc(loc buffer.Loc) VLoc {
|
||||
vloc := VLoc{SLoc: SLoc{loc.Y, 0}, VisualX: 0}
|
||||
|
||||
if loc.X <= 0 {
|
||||
return vloc
|
||||
}
|
||||
|
||||
if w.bufWidth <= 0 {
|
||||
return vloc
|
||||
}
|
||||
|
||||
wordwrap := w.Buf.Settings["wordwrap"].(bool)
|
||||
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
|
||||
|
||||
line := w.Buf.LineBytes(loc.Y)
|
||||
x := 0
|
||||
totalwidth := 0
|
||||
|
||||
wordwidth := 0
|
||||
wordoffset := 0
|
||||
|
||||
for len(line) > 0 {
|
||||
r, _, size := util.DecodeCharacter(line)
|
||||
line = line[size:]
|
||||
|
||||
width := 0
|
||||
switch r {
|
||||
case '\t':
|
||||
ts := tabsize - (totalwidth % tabsize)
|
||||
width = util.Min(ts, w.bufWidth-vloc.VisualX)
|
||||
totalwidth += ts
|
||||
default:
|
||||
width = runewidth.RuneWidth(r)
|
||||
totalwidth += width
|
||||
}
|
||||
|
||||
wordwidth += width
|
||||
|
||||
// Collect a complete word to know its width.
|
||||
// If wordwrap is off, every single character is a complete "word".
|
||||
if wordwrap {
|
||||
if !util.IsWhitespace(r) && len(line) > 0 && wordwidth < w.bufWidth {
|
||||
if x < loc.X {
|
||||
wordoffset += width
|
||||
x++
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// If a word (or just a wide rune) does not fit in the window
|
||||
if vloc.VisualX+wordwidth > w.bufWidth && vloc.VisualX > 0 {
|
||||
vloc.Row++
|
||||
vloc.VisualX = 0
|
||||
}
|
||||
|
||||
if x == loc.X {
|
||||
vloc.VisualX += wordoffset
|
||||
return vloc
|
||||
}
|
||||
x++
|
||||
|
||||
vloc.VisualX += wordwidth
|
||||
|
||||
wordwidth = 0
|
||||
wordoffset = 0
|
||||
|
||||
if vloc.VisualX >= w.bufWidth {
|
||||
vloc.Row++
|
||||
vloc.VisualX = 0
|
||||
}
|
||||
}
|
||||
return vloc
|
||||
}
|
||||
|
||||
func (w *BufWindow) getLocFromVLoc(svloc VLoc) buffer.Loc {
|
||||
loc := buffer.Loc{X: 0, Y: svloc.Line}
|
||||
|
||||
if w.bufWidth <= 0 {
|
||||
return loc
|
||||
}
|
||||
|
||||
wordwrap := w.Buf.Settings["wordwrap"].(bool)
|
||||
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
|
||||
|
||||
line := w.Buf.LineBytes(svloc.Line)
|
||||
vloc := VLoc{SLoc: SLoc{svloc.Line, 0}, VisualX: 0}
|
||||
|
||||
totalwidth := 0
|
||||
|
||||
var widths []int
|
||||
if wordwrap {
|
||||
widths = make([]int, 0, w.bufWidth)
|
||||
} else {
|
||||
widths = make([]int, 0, 1)
|
||||
}
|
||||
wordwidth := 0
|
||||
|
||||
for len(line) > 0 {
|
||||
r, _, size := util.DecodeCharacter(line)
|
||||
line = line[size:]
|
||||
|
||||
width := 0
|
||||
switch r {
|
||||
case '\t':
|
||||
ts := tabsize - (totalwidth % tabsize)
|
||||
width = util.Min(ts, w.bufWidth-vloc.VisualX)
|
||||
totalwidth += ts
|
||||
default:
|
||||
width = runewidth.RuneWidth(r)
|
||||
totalwidth += width
|
||||
}
|
||||
|
||||
widths = append(widths, width)
|
||||
wordwidth += width
|
||||
|
||||
// Collect a complete word to know its width.
|
||||
// If wordwrap is off, every single character is a complete "word".
|
||||
if wordwrap {
|
||||
if !util.IsWhitespace(r) && len(line) > 0 && wordwidth < w.bufWidth {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// If a word (or just a wide rune) does not fit in the window
|
||||
if vloc.VisualX+wordwidth > w.bufWidth && vloc.VisualX > 0 {
|
||||
if vloc.Row == svloc.Row {
|
||||
if wordwrap {
|
||||
// it's a word, not a wide rune
|
||||
loc.X--
|
||||
}
|
||||
return loc
|
||||
}
|
||||
vloc.Row++
|
||||
vloc.VisualX = 0
|
||||
}
|
||||
|
||||
for i := range widths {
|
||||
vloc.VisualX += widths[i]
|
||||
if vloc.Row == svloc.Row && vloc.VisualX > svloc.VisualX {
|
||||
return loc
|
||||
}
|
||||
loc.X++
|
||||
}
|
||||
|
||||
widths = widths[:0]
|
||||
wordwidth = 0
|
||||
|
||||
if vloc.VisualX >= w.bufWidth {
|
||||
vloc.Row++
|
||||
vloc.VisualX = 0
|
||||
}
|
||||
}
|
||||
return loc
|
||||
}
|
||||
|
||||
func (w *BufWindow) getRowCount(line int) int {
|
||||
eol := buffer.Loc{X: util.CharacterCount(w.Buf.LineBytes(line)), Y: line}
|
||||
return w.getVLocFromLoc(eol).Row + 1
|
||||
}
|
||||
|
||||
func (w *BufWindow) scrollUp(s SLoc, n int) SLoc {
|
||||
for n > 0 {
|
||||
if n <= s.Row {
|
||||
s.Row -= n
|
||||
n = 0
|
||||
} else if s.Line > 0 {
|
||||
s.Line--
|
||||
n -= s.Row + 1
|
||||
s.Row = w.getRowCount(s.Line) - 1
|
||||
} else {
|
||||
s.Row = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (w *BufWindow) scrollDown(s SLoc, n int) SLoc {
|
||||
for n > 0 {
|
||||
rc := w.getRowCount(s.Line)
|
||||
if n < rc-s.Row {
|
||||
s.Row += n
|
||||
n = 0
|
||||
} else if s.Line < w.Buf.LinesNum()-1 {
|
||||
s.Line++
|
||||
n -= rc - s.Row
|
||||
s.Row = 0
|
||||
} else {
|
||||
s.Row = rc - 1
|
||||
break
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (w *BufWindow) scroll(s SLoc, n int) SLoc {
|
||||
if n < 0 {
|
||||
return w.scrollUp(s, -n)
|
||||
}
|
||||
return w.scrollDown(s, n)
|
||||
}
|
||||
|
||||
func (w *BufWindow) diff(s1, s2 SLoc) int {
|
||||
n := 0
|
||||
for s1.LessThan(s2) {
|
||||
if s1.Line < s2.Line {
|
||||
n += w.getRowCount(s1.Line) - s1.Row
|
||||
s1.Line++
|
||||
s1.Row = 0
|
||||
} else {
|
||||
n += s2.Row - s1.Row
|
||||
s1.Row = s2.Row
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Scroll returns the location which is n visual lines below the location s
|
||||
// i.e. the result of scrolling n lines down. n can be negative,
|
||||
// which means scrolling up. The returned location is guaranteed to be
|
||||
// within the buffer boundaries.
|
||||
func (w *BufWindow) Scroll(s SLoc, n int) SLoc {
|
||||
if !w.Buf.Settings["softwrap"].(bool) {
|
||||
s.Line += n
|
||||
if s.Line < 0 {
|
||||
s.Line = 0
|
||||
}
|
||||
if s.Line > w.Buf.LinesNum()-1 {
|
||||
s.Line = w.Buf.LinesNum() - 1
|
||||
}
|
||||
return s
|
||||
}
|
||||
return w.scroll(s, n)
|
||||
}
|
||||
|
||||
// Diff returns the difference (the vertical distance) between two SLocs.
|
||||
func (w *BufWindow) Diff(s1, s2 SLoc) int {
|
||||
if !w.Buf.Settings["softwrap"].(bool) {
|
||||
return s2.Line - s1.Line
|
||||
}
|
||||
if s1.GreaterThan(s2) {
|
||||
return -w.diff(s2, s1)
|
||||
}
|
||||
return w.diff(s1, s2)
|
||||
}
|
||||
|
||||
// SLocFromLoc takes a position in the buffer and returns the location
|
||||
// of the visual line containing this position.
|
||||
func (w *BufWindow) SLocFromLoc(loc buffer.Loc) SLoc {
|
||||
if !w.Buf.Settings["softwrap"].(bool) {
|
||||
return SLoc{loc.Y, 0}
|
||||
}
|
||||
return w.getVLocFromLoc(loc).SLoc
|
||||
}
|
||||
|
||||
// VLocFromLoc takes a position in the buffer and returns the corresponding
|
||||
// visual location in the linewrapped buffer.
|
||||
func (w *BufWindow) VLocFromLoc(loc buffer.Loc) VLoc {
|
||||
if !w.Buf.Settings["softwrap"].(bool) {
|
||||
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
|
||||
|
||||
visualx := util.StringWidth(w.Buf.LineBytes(loc.Y), loc.X, tabsize)
|
||||
return VLoc{SLoc{loc.Y, 0}, visualx}
|
||||
}
|
||||
return w.getVLocFromLoc(loc)
|
||||
}
|
||||
|
||||
// LocFromVLoc takes a visual location in the linewrapped buffer and returns
|
||||
// the position in the buffer corresponding to this visual location.
|
||||
func (w *BufWindow) LocFromVLoc(vloc VLoc) buffer.Loc {
|
||||
if !w.Buf.Settings["softwrap"].(bool) {
|
||||
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
|
||||
|
||||
x := util.GetCharPosInLine(w.Buf.LineBytes(vloc.Line), vloc.VisualX, tabsize)
|
||||
return buffer.Loc{x, vloc.Line}
|
||||
}
|
||||
return w.getLocFromVLoc(vloc)
|
||||
}
|
||||
@@ -98,6 +98,8 @@ func (s *StatusLine) Display() {
|
||||
// We'll draw the line at the lowest line in the window
|
||||
y := s.win.Height + s.win.Y - 1
|
||||
|
||||
winX := s.win.X
|
||||
|
||||
b := s.win.Buf
|
||||
// autocomplete suggestions (for the buffer, not for the infowindow)
|
||||
if b.HasSuggestions && len(b.Suggestions) > 1 {
|
||||
@@ -105,10 +107,6 @@ func (s *StatusLine) Display() {
|
||||
if style, ok := config.Colorscheme["statusline"]; ok {
|
||||
statusLineStyle = style
|
||||
}
|
||||
keymenuOffset := 0
|
||||
if config.GetGlobalOption("keymenu").(bool) {
|
||||
keymenuOffset = len(keydisplay)
|
||||
}
|
||||
x := 0
|
||||
for j, sug := range b.Suggestions {
|
||||
style := statusLineStyle
|
||||
@@ -116,13 +114,13 @@ func (s *StatusLine) Display() {
|
||||
style = style.Reverse(true)
|
||||
}
|
||||
for _, r := range sug {
|
||||
screen.SetContent(x, y-keymenuOffset, r, nil, style)
|
||||
screen.SetContent(winX+x, y, r, nil, style)
|
||||
x++
|
||||
if x >= s.win.Width {
|
||||
return
|
||||
}
|
||||
}
|
||||
screen.SetContent(x, y-keymenuOffset, ' ', nil, statusLineStyle)
|
||||
screen.SetContent(winX+x, y, ' ', nil, statusLineStyle)
|
||||
x++
|
||||
if x >= s.win.Width {
|
||||
return
|
||||
@@ -130,7 +128,7 @@ func (s *StatusLine) Display() {
|
||||
}
|
||||
|
||||
for x < s.win.Width {
|
||||
screen.SetContent(x, y-keymenuOffset, ' ', nil, statusLineStyle)
|
||||
screen.SetContent(winX+x, y, ' ', nil, statusLineStyle)
|
||||
x++
|
||||
}
|
||||
return
|
||||
@@ -143,7 +141,7 @@ func (s *StatusLine) Display() {
|
||||
return []byte(fmt.Sprint(s.FindOpt(string(option))))
|
||||
} else if bytes.HasPrefix(name, []byte("bind")) {
|
||||
binding := string(name[5:])
|
||||
for k, v := range config.Bindings {
|
||||
for k, v := range config.Bindings["buffer"] {
|
||||
if v == binding {
|
||||
return []byte(k)
|
||||
}
|
||||
@@ -170,7 +168,6 @@ func (s *StatusLine) Display() {
|
||||
leftLen := util.StringWidth(leftText, util.CharacterCount(leftText), 1)
|
||||
rightLen := util.StringWidth(rightText, util.CharacterCount(rightText), 1)
|
||||
|
||||
winX := s.win.X
|
||||
for x := 0; x < s.win.Width; x++ {
|
||||
if x < leftLen {
|
||||
r, combc, size := util.DecodeCharacter(leftText)
|
||||
|
||||
@@ -98,8 +98,16 @@ func (w *TabWindow) Display() {
|
||||
if style, ok := config.Colorscheme["tabbar"]; ok {
|
||||
tabBarStyle = style
|
||||
}
|
||||
tabBarActiveStyle := tabBarStyle
|
||||
if style, ok := config.Colorscheme["tabbar.active"]; ok {
|
||||
tabBarActiveStyle = style
|
||||
}
|
||||
|
||||
draw := func(r rune, n int) {
|
||||
draw := func(r rune, n int, active bool) {
|
||||
style := tabBarStyle
|
||||
if active {
|
||||
style = tabBarActiveStyle
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
rw := runewidth.RuneWidth(r)
|
||||
for j := 0; j < rw; j++ {
|
||||
@@ -114,7 +122,7 @@ func (w *TabWindow) Display() {
|
||||
} else if x == 0 && w.hscroll > 0 {
|
||||
screen.SetContent(0, w.Y, '<', nil, tabBarStyle)
|
||||
} else if x >= 0 && x < w.Width {
|
||||
screen.SetContent(x, w.Y, c, nil, tabBarStyle)
|
||||
screen.SetContent(x, w.Y, c, nil, style)
|
||||
}
|
||||
x++
|
||||
}
|
||||
@@ -123,21 +131,21 @@ func (w *TabWindow) Display() {
|
||||
|
||||
for i, n := range w.Names {
|
||||
if i == w.active {
|
||||
draw('[', 1)
|
||||
draw('[', 1, true)
|
||||
} else {
|
||||
draw(' ', 1)
|
||||
draw(' ', 1, false)
|
||||
}
|
||||
for _, c := range n {
|
||||
draw(c, 1)
|
||||
draw(c, 1, i == w.active)
|
||||
}
|
||||
if i == len(w.Names)-1 {
|
||||
done = true
|
||||
}
|
||||
if i == w.active {
|
||||
draw(']', 1)
|
||||
draw(' ', 2)
|
||||
draw(']', 1, true)
|
||||
draw(' ', 2, true)
|
||||
} else {
|
||||
draw(' ', 3)
|
||||
draw(' ', 3, false)
|
||||
}
|
||||
if x >= w.Width {
|
||||
break
|
||||
@@ -145,6 +153,6 @@ func (w *TabWindow) Display() {
|
||||
}
|
||||
|
||||
if x < w.Width {
|
||||
draw(' ', w.Width-x)
|
||||
draw(' ', w.Width-x, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func (w *TermWindow) Display() {
|
||||
if b == terminal.DefaultBG {
|
||||
bg = int(tcell.ColorDefault)
|
||||
}
|
||||
st := tcell.StyleDefault.Foreground(config.GetColor256(int(fg))).Background(config.GetColor256(int(bg)))
|
||||
st := tcell.StyleDefault.Foreground(config.GetColor256(fg)).Background(config.GetColor256(bg))
|
||||
|
||||
if l.LessThan(w.Selection[1]) && l.GreaterEqual(w.Selection[0]) || l.LessThan(w.Selection[0]) && l.GreaterEqual(w.Selection[1]) {
|
||||
st = st.Reverse(true)
|
||||
|
||||
@@ -8,10 +8,13 @@ type View struct {
|
||||
X, Y int // X,Y location of the view
|
||||
Width, Height int // Width and height of the view
|
||||
|
||||
// Start line and start column of the view (vertical/horizontal scroll)
|
||||
// Start line of the view (for vertical scroll)
|
||||
StartLine SLoc
|
||||
|
||||
// Start column of the view (for horizontal scroll)
|
||||
// note that since the starting column of every line is different if the view
|
||||
// is scrolled, StartCol is a visual index (will be the same for every line)
|
||||
StartLine, StartCol int
|
||||
StartCol int
|
||||
}
|
||||
|
||||
type Window interface {
|
||||
@@ -28,5 +31,7 @@ type Window interface {
|
||||
|
||||
type BWindow interface {
|
||||
Window
|
||||
SoftWrap
|
||||
SetBuffer(b *buffer.Buffer)
|
||||
BufView() View
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
func (i *InfoBuf) LoadHistory() {
|
||||
if config.GetGlobalOption("savehistory").(bool) {
|
||||
file, err := os.Open(filepath.Join(config.ConfigDir, "buffers", "history"))
|
||||
defer file.Close()
|
||||
var decodedMap map[string][]string
|
||||
if err == nil {
|
||||
defer file.Close()
|
||||
decoder := gob.NewDecoder(file)
|
||||
err = decoder.Decode(&decodedMap)
|
||||
|
||||
@@ -48,8 +48,8 @@ func (i *InfoBuf) SaveHistory() {
|
||||
}
|
||||
|
||||
file, err := os.Create(filepath.Join(config.ConfigDir, "buffers", "history"))
|
||||
defer file.Close()
|
||||
if err == nil {
|
||||
defer file.Close()
|
||||
encoder := gob.NewEncoder(file)
|
||||
|
||||
err = encoder.Encode(i.History)
|
||||
|
||||
@@ -54,7 +54,7 @@ func (i *InfoBuf) Close() {
|
||||
func (i *InfoBuf) Message(msg ...interface{}) {
|
||||
// only display a new message if there isn't an active prompt
|
||||
// this is to prevent overwriting an existing prompt to the user
|
||||
if i.HasPrompt == false {
|
||||
if !i.HasPrompt {
|
||||
displayMessage := fmt.Sprint(msg...)
|
||||
// if there is no active prompt then style and display the message as normal
|
||||
i.Msg = displayMessage
|
||||
@@ -78,7 +78,7 @@ func (i *InfoBuf) ClearGutter() {
|
||||
func (i *InfoBuf) Error(msg ...interface{}) {
|
||||
// only display a new message if there isn't an active prompt
|
||||
// this is to prevent overwriting an existing prompt to the user
|
||||
if i.HasPrompt == false {
|
||||
if !i.HasPrompt {
|
||||
// if there is no active prompt then style and display the message as normal
|
||||
i.Msg = fmt.Sprint(msg...)
|
||||
i.HasMessage, i.HasError = false, true
|
||||
@@ -137,11 +137,13 @@ func (i *InfoBuf) DonePrompt(canceled bool) {
|
||||
if !hadYN {
|
||||
if i.PromptCallback != nil {
|
||||
if canceled {
|
||||
i.Replace(i.Start(), i.End(), "")
|
||||
i.PromptCallback("", true)
|
||||
h := i.History[i.PromptType]
|
||||
i.History[i.PromptType] = h[:len(h)-1]
|
||||
} else {
|
||||
resp := string(i.LineBytes(0))
|
||||
i.Replace(i.Start(), i.End(), "")
|
||||
i.PromptCallback(resp, false)
|
||||
h := i.History[i.PromptType]
|
||||
h[len(h)-1] = resp
|
||||
@@ -156,7 +158,6 @@ func (i *InfoBuf) DonePrompt(canceled bool) {
|
||||
}
|
||||
// i.PromptCallback = nil
|
||||
}
|
||||
i.Replace(i.Start(), i.End(), "")
|
||||
}
|
||||
if i.YNCallback != nil && hadYN {
|
||||
i.YNCallback(i.YNResp, canceled)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package lua
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -74,6 +76,10 @@ func Import(pkg string) *lua.LTable {
|
||||
return importUtf8()
|
||||
case "humanize":
|
||||
return importHumanize()
|
||||
case "net/http", "http":
|
||||
return importHTTP()
|
||||
case "archive/zip":
|
||||
return importArchiveZip()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -383,6 +389,7 @@ func importOs() *lua.LTable {
|
||||
L.SetField(pkg, "Symlink", luar.New(L, os.Symlink))
|
||||
L.SetField(pkg, "TempDir", luar.New(L, os.TempDir))
|
||||
L.SetField(pkg, "Truncate", luar.New(L, os.Truncate))
|
||||
L.SetField(pkg, "UserHomeDir", luar.New(L, os.UserHomeDir))
|
||||
|
||||
return pkg
|
||||
}
|
||||
@@ -570,3 +577,22 @@ func importHumanize() *lua.LTable {
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importHTTP() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "Get", luar.New(L, http.Get))
|
||||
L.SetField(pkg, "Post", luar.New(L, http.Post))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func importArchiveZip() *lua.LTable {
|
||||
pkg := L.NewTable()
|
||||
|
||||
L.SetField(pkg, "OpenReader", luar.New(L, zip.OpenReader))
|
||||
L.SetField(pkg, "NewReader", luar.New(L, zip.NewReader))
|
||||
L.SetField(pkg, "NewWriter", luar.New(L, zip.NewWriter))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package screen
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
@@ -144,16 +145,28 @@ func Init() error {
|
||||
}
|
||||
|
||||
var oldTerm string
|
||||
if config.GetGlobalOption("xterm").(bool) {
|
||||
modifiedTerm := false
|
||||
setXterm := func() {
|
||||
oldTerm = os.Getenv("TERM")
|
||||
os.Setenv("TERM", "xterm-256color")
|
||||
modifiedTerm = true
|
||||
}
|
||||
|
||||
if config.GetGlobalOption("xterm").(bool) {
|
||||
setXterm()
|
||||
}
|
||||
|
||||
// Initilize tcell
|
||||
var err error
|
||||
Screen, err = tcell.NewScreen()
|
||||
if err != nil {
|
||||
return err
|
||||
log.Println("Warning: during screen initialization:", err)
|
||||
log.Println("Falling back to TERM=xterm-256color")
|
||||
setXterm()
|
||||
Screen, err = tcell.NewScreen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = Screen.Init(); err != nil {
|
||||
return err
|
||||
@@ -162,7 +175,7 @@ func Init() error {
|
||||
Screen.SetPaste(config.GetGlobalOption("paste").(bool))
|
||||
|
||||
// restore TERM
|
||||
if config.GetGlobalOption("xterm").(bool) {
|
||||
if modifiedTerm {
|
||||
os.Setenv("TERM", oldTerm)
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit func(
|
||||
go func() {
|
||||
// Run the process in the background and create the onExit callback
|
||||
proc.Run()
|
||||
jobFunc := JobFunction{onExit, string(outbuf.Bytes()), userargs}
|
||||
jobFunc := JobFunction{onExit, outbuf.String(), userargs}
|
||||
Jobs <- jobFunc
|
||||
}()
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
@@ -46,7 +48,8 @@ func init() {
|
||||
fmt.Println("Invalid version: ", Version, err)
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
_, wt := os.LookupEnv("WT_SESSION")
|
||||
if runtime.GOOS == "windows" && !wt {
|
||||
FakeCursor = true
|
||||
}
|
||||
Stdout = new(bytes.Buffer)
|
||||
@@ -338,9 +341,9 @@ func EscapePath(path string) string {
|
||||
path = filepath.ToSlash(path)
|
||||
if runtime.GOOS == "windows" {
|
||||
// ':' is not valid in a path name on Windows but is ok on Unix
|
||||
path = strings.Replace(path, ":", "%", -1)
|
||||
path = strings.ReplaceAll(path, ":", "%")
|
||||
}
|
||||
return strings.Replace(path, "/", "%", -1)
|
||||
return strings.ReplaceAll(path, "/", "%")
|
||||
}
|
||||
|
||||
// GetLeadingWhitespace returns the leading whitespace of the given byte array
|
||||
@@ -427,10 +430,63 @@ func IsAutocomplete(c rune) bool {
|
||||
}
|
||||
|
||||
func ParseSpecial(s string) string {
|
||||
return strings.Replace(s, "\\t", "\t", -1)
|
||||
return strings.ReplaceAll(s, "\\t", "\t")
|
||||
}
|
||||
|
||||
// String converts a byte array to a string (for lua plugins)
|
||||
func String(s []byte) string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// Unzip unzips a file to given folder
|
||||
func Unzip(src, dest string) error {
|
||||
r, err := zip.OpenReader(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
os.MkdirAll(dest, 0755)
|
||||
|
||||
// Closure to address file descriptors issue with all the deferred .Close() methods
|
||||
extractAndWriteFile := func(f *zip.File) error {
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
path := filepath.Join(dest, f.Name)
|
||||
|
||||
// Check for ZipSlip (Directory traversal)
|
||||
if !strings.HasPrefix(path, filepath.Clean(dest)+string(os.PathSeparator)) {
|
||||
return fmt.Errorf("illegal file path: %s", path)
|
||||
}
|
||||
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(path, f.Mode())
|
||||
} else {
|
||||
os.MkdirAll(filepath.Dir(path), f.Mode())
|
||||
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = io.Copy(f, rc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, f := range r.File {
|
||||
err := extractAndWriteFile(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ color-link constant "#AE81FF,#282828"
|
||||
color-link constant.string "#E6DB74,#282828"
|
||||
color-link constant.string.char "#BDE6AD,#282828"
|
||||
color-link statement "#F92672,#282828"
|
||||
color-link symbol "#F92672,#282828"
|
||||
color-link symbol.operator "#F92671,#282828"
|
||||
color-link preproc "#CB4B16,#282828"
|
||||
color-link type "#66D9EF,#282828"
|
||||
color-link special "#A6E22E,#282828"
|
||||
|
||||
43
runtime/colorschemes/dracula-tc.micro
Normal file
43
runtime/colorschemes/dracula-tc.micro
Normal file
@@ -0,0 +1,43 @@
|
||||
color-link default "#F8F8F2,#282A36"
|
||||
color-link comment "#6272A4"
|
||||
|
||||
color-link identifier "#50FA7B"
|
||||
color-link identifier.class "#8BE9FD"
|
||||
color-link identifier.var "#F8F8F2"
|
||||
|
||||
color-link constant "#BD93F9"
|
||||
color-link constant.number "#F8F8F2"
|
||||
color-link constant.string "#F1FA8C"
|
||||
|
||||
color-link symbol "#FF79C6"
|
||||
color-link symbol.brackets "#F8F8F2"
|
||||
color-link symbol.tag "#AE81FF"
|
||||
|
||||
color-link type "italic #8BE9FD"
|
||||
color-link type.keyword "#FF79C6"
|
||||
|
||||
color-link special "#FF79C6"
|
||||
color-link statement "#FF79C6"
|
||||
color-link preproc "#FF79C6"
|
||||
|
||||
color-link underlined "#FF79C6"
|
||||
color-link error "bold #FF5555"
|
||||
color-link todo "bold #FF79C6"
|
||||
|
||||
color-link diff-added "#50FA7B"
|
||||
color-link diff-modified "#FFB86C"
|
||||
color-link diff-deleted "#FF5555"
|
||||
|
||||
color-link gutter-error "#FF5555"
|
||||
color-link gutter-warning "#E6DB74"
|
||||
|
||||
color-link statusline "#282A36,#F8F8F2"
|
||||
color-link tabbar "#282A36,#F8F8F2"
|
||||
color-link indent-char "#6272A4"
|
||||
color-link line-number "#6272A4"
|
||||
color-link current-line-number "#F8F8F2"
|
||||
|
||||
color-link cursor-line "#44475A,#F8F8F2"
|
||||
color-link color-column "#44475A"
|
||||
color-link type.extended "default"
|
||||
|
||||
@@ -5,7 +5,7 @@ color-link constant "#AE81FF,#282828"
|
||||
color-link constant.string "#E6DB74,#282828"
|
||||
color-link constant.string.char "#BDE6AD,#282828"
|
||||
color-link statement "#F92672,#282828"
|
||||
color-link symbol "#F92672,#282828"
|
||||
color-link symbol.operator "#F92672,#282828"
|
||||
color-link preproc "#CB4B16,#282828"
|
||||
color-link type "#66D9EF,#282828"
|
||||
color-link special "#A6E22E,#282828"
|
||||
|
||||
@@ -87,7 +87,8 @@ These may vary widely based on the 16 colors selected for your terminal.
|
||||
True color requires your terminal to support it. This means that the
|
||||
environment variable `COLORTERM` should have the value `truecolor`, `24bit`,
|
||||
or `24-bit`. In addition, to enable true color in micro, the environment
|
||||
variable `MICRO_TRUECOLOR` must be set to 1.
|
||||
variable `MICRO_TRUECOLOR` must be set to 1. Note that you have to create
|
||||
and set this variable yourself.
|
||||
|
||||
* `solarized-tc`: this is the solarized colorscheme for true color.
|
||||
* `atom-dark-tc`: this colorscheme is based off of Atom's "dark" colorscheme.
|
||||
@@ -175,6 +176,7 @@ Here is a list of the colorscheme groups that you can use:
|
||||
* underlined
|
||||
* error
|
||||
* todo
|
||||
* selection (Color of the text selection)
|
||||
* statusline (Color of the statusline)
|
||||
* tabbar (Color of the tabbar that lists open files)
|
||||
* indent-char (Color of the character which indicates tabs if the option is
|
||||
@@ -182,11 +184,17 @@ Here is a list of the colorscheme groups that you can use:
|
||||
* line-number
|
||||
* gutter-error
|
||||
* gutter-warning
|
||||
* diff-added
|
||||
* diff-modified
|
||||
* diff-deleted
|
||||
* cursor-line
|
||||
* current-line-number
|
||||
* color-column
|
||||
* ignore
|
||||
* scrollbar
|
||||
* 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)
|
||||
|
||||
Colorschemes must be placed in the `~/.config/micro/colorschemes` directory to
|
||||
be used.
|
||||
|
||||
@@ -159,9 +159,13 @@ Here are the available options:
|
||||
default value: `unknown`. This will be automatically overridden depending
|
||||
on the file you open.
|
||||
|
||||
* `incsearch`: enable incremental search in "Find" prompt (matching as you type).
|
||||
|
||||
default value: `true`
|
||||
|
||||
* `ignorecase`: perform case-insensitive searches.
|
||||
|
||||
default value: `false`
|
||||
default value: `true`
|
||||
|
||||
* `indentchar`: sets the indentation character.
|
||||
|
||||
@@ -237,8 +241,7 @@ Here are the available options:
|
||||
By default, this option points to the official plugin channel hosted on GitHub
|
||||
at https://github.com/micro-editor/plugin-channel.
|
||||
|
||||
default value: `https://raw.githubusercontent.com/micro-editor/plugin-channel
|
||||
/master/channel.json`
|
||||
default value: `https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json`
|
||||
|
||||
* `pluginrepos`: a list of links to plugin repositories.
|
||||
|
||||
@@ -362,6 +365,11 @@ Here are the available options:
|
||||
|
||||
default value: `true`
|
||||
|
||||
* `wordwrap`: wrap long lines by words, i.e. break at spaces. This option
|
||||
only does anything if `softwrap` is on.
|
||||
|
||||
default value: `false`
|
||||
|
||||
* `xterm`: micro will assume that the terminal it is running in conforms to
|
||||
`xterm-256color` regardless of what the `$TERM` variable actually contains.
|
||||
Enabling this option may cause unwanted effects if your terminal in fact
|
||||
@@ -424,6 +432,7 @@ so that you can see what the formatting should look like.
|
||||
"fastdirty": false,
|
||||
"fileformat": "unix",
|
||||
"filetype": "unknown",
|
||||
"incsearch": true,
|
||||
"ftoptions": true,
|
||||
"ignorecase": false,
|
||||
"indentchar": " ",
|
||||
@@ -466,7 +475,6 @@ so that you can see what the formatting should look like.
|
||||
"tabmovement": false,
|
||||
"tabsize": 4,
|
||||
"tabstospaces": false,
|
||||
"test": true,
|
||||
"useprimary": true,
|
||||
"xterm": false
|
||||
}
|
||||
|
||||
@@ -259,6 +259,7 @@ The packages and functions are listed below (in Go type signatures):
|
||||
- `MTError` error message.
|
||||
|
||||
- `Loc(x, y int) Loc`: creates a new location struct.
|
||||
- `SLoc(line, row int) display.SLoc`: creates a new scrolling location struct.
|
||||
|
||||
- `BTDefault`: default buffer type.
|
||||
- `BTLog`: log buffer type.
|
||||
@@ -285,6 +286,7 @@ The packages and functions are listed below (in Go type signatures):
|
||||
string is a word character.
|
||||
- `String(b []byte) string`: converts a byte array to a string.
|
||||
- `RuneStr(r rune) string`: converts a rune to a string.
|
||||
- `Unzip(src, dest string) error`: unzips a file to given folder.
|
||||
|
||||
This may seem like a small list of available functions but some of the objects
|
||||
returned by the functions have many methods. The Lua plugin may access any
|
||||
@@ -358,6 +360,8 @@ strings
|
||||
regexp
|
||||
errors
|
||||
time
|
||||
archive/zip
|
||||
net/http
|
||||
```
|
||||
|
||||
For documentation for each of these functions, see the Go standard
|
||||
@@ -418,7 +422,7 @@ your own plugins.
|
||||
Micro also has a built in plugin manager which you can invoke with the
|
||||
`> plugin ...` command, or in the shell with `micro -plugin ...`.
|
||||
|
||||
For the valid commands you can use, see the `command` help topic.
|
||||
For the valid commands you can use, see the `commands` help topic.
|
||||
|
||||
The manager fetches plugins from the channels (which is simply a list of plugin
|
||||
metadata) which it knows about. By default, micro only knows about the official
|
||||
|
||||
@@ -71,7 +71,7 @@ function preinit()
|
||||
makeLinter("eslint", "javascript", "eslint", {"-f","compact","%f"}, "%f: line %l, col %c, %m")
|
||||
makeLinter("gobuild", "go", "go", {"build", "-o", devnull, "%d"}, "%f:%l:%c:? %m")
|
||||
-- makeLinter("golint", "go", "golint", {"%f"}, "%f:%l:%c: %m")
|
||||
makeLinter("hlint", "haskell", "hlint", {"%f"}, "%f:%l:%c.-: %m")
|
||||
makeLinter("hlint", "haskell", "hlint", {"%f"}, "%f:%(?%l[,:]%c%)?.-: %m")
|
||||
makeLinter("javac", "java", "javac", {"-d", "%d", "%f"}, "%f:%l: error: %m")
|
||||
makeLinter("jshint", "javascript", "jshint", {"%f"}, "%f: line %l,.+, %m")
|
||||
makeLinter("literate", "literate", "lit", {"-c", "%f"}, "%f:%l:%m", {}, false, true)
|
||||
|
||||
@@ -5,7 +5,7 @@ detect:
|
||||
|
||||
rules:
|
||||
- identifier: "\\b[A-Z_][0-9A-Z_]+\\b"
|
||||
- type: "\\b(float|double|char|int|short|long|sizeof|enum|void|static|const|struct|union|typedef|extern|(un)?signed|inline)\\b"
|
||||
- type: "\\b(auto|float|double|char|int|short|long|sizeof|enum|void|static|const|struct|union|typedef|extern|(un)?signed|inline)\\b"
|
||||
- type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr)))_t\\b"
|
||||
- type.extended: "\\b(bool)\\b"
|
||||
- statement: "\\b(volatile|register)\\b"
|
||||
@@ -18,7 +18,12 @@ rules:
|
||||
# Operator Color
|
||||
- symbol.operator: "([.:;,+*|=!\\%]|<|>|/|-|&)"
|
||||
- symbol.brackets: "[(){}]|\\[|\\]"
|
||||
- constant.number: "(\\b[0-9]+\\b|\\b0x[0-9A-Fa-f]+\\b)"
|
||||
# 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)"
|
||||
# Decimal Floating Constants
|
||||
- constant.number: "(\\b(([0-9]*[.][0-9]+|[0-9]+[.][0-9]*)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)[FfLl]?\\b)"
|
||||
# Hexadecimal Floating Constants
|
||||
- constant.number: "(\\b0[Xx]([0-9A-Za-z]*[.][0-9A-Za-z]+|[0-9A-Za-z]+[.][0-9A-Za-z]*)[Pp][+-]?[0-9]+[FfLl]?\\b)"
|
||||
- constant.number: "NULL"
|
||||
|
||||
- constant.string:
|
||||
|
||||
@@ -4,19 +4,32 @@ detect:
|
||||
filename: "\\.coffee$"
|
||||
|
||||
rules:
|
||||
- symbol.operator: "[!&|=/*+-<>]|\\b(and|or|is|isnt|not)\\b"
|
||||
- symbol.operator: "([-+/*=<>!~%?:&|]|[.]{3})|\\b(and|or|is|isnt|not)\\b"
|
||||
- identifier.class: "([A-Za-z_][A-Za-z0-9_]*:[[:space:]]*(->|\\()|->)"
|
||||
- symbol.brackets: "[()]"
|
||||
- statement: "\\b(for|of|continue|break|isnt|null|unless|this|else|if|return)\\b"
|
||||
- statement: "\\b(try|catch|finally|throw|new|delete|typeof|in|instanceof)\\b"
|
||||
- statement: "\\b(debugger|switch|while|do|class|extends|super)\\b"
|
||||
- statement: "\\b(undefined|then|unless|until|loop|of|by|when)\\b"
|
||||
|
||||
- statement: "\\b(await|when|catch|continue|debugger|default|by|until)\\b"
|
||||
- statement: "\\b(delete|do|else|export|finally|for|class|extends|while|then)\\b"
|
||||
- statement: "\\b(get|if|import|from|in|instanceof|new|reject|resolve|return)\\b"
|
||||
- statement: "\\b(set|super|switch|this|throw|try|typeof|with|yield|unless)\\b"
|
||||
|
||||
- constant.bool: "\\b(true|false|yes|no|on|off)\\b"
|
||||
- constant.bool.false: "\\b(false|no|off)\\b"
|
||||
- constant.bool.true: "\\b(true|yes|on)\\b"
|
||||
|
||||
- constant.number: "\\b[-+]?([1-9][0-9]*|0[0-7]*|0x[0-9a-fA-F]+)([uU][lL]?|[lL][uU]?)?\\b"
|
||||
- constant.number: "\\b[-+]?([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)([EePp][+-]?[0-9]+)?[fFlL]?"
|
||||
- constant.number: "\\b[-+]?([0-9]+[EePp][+-]?[0-9]+)[fFlL]?"
|
||||
- identifier: "@[A-Za-z0-9_]*"
|
||||
|
||||
- error: "\\b(enum|implements|interface|package|private|protected|public)"
|
||||
- constant: "\\b(globalThis|Infinity|null|undefined|NaN)\\b"
|
||||
- constant: "\\b(null|undefined|NaN)\\b"
|
||||
- constant: "\\b(true|false|yes|no|on|off)\\b"
|
||||
- type: "\\b(Array|Boolean|Date|Enumerator|Error|Function|Generator|Map|Math)\\b"
|
||||
- type: "\\b(Number|Object|Promise|Proxy|Reflect|RegExp|Set|String|Symbol|WeakMap|WeakSet)\\b"
|
||||
- type: "\\b(BigInt64Array|BigUint64Array|Float32Array|Float64Array|Int16Array)\\b"
|
||||
|
||||
- constant.string:
|
||||
start: "\""
|
||||
end: "\""
|
||||
@@ -30,16 +43,14 @@ rules:
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "\\\\."
|
||||
|
||||
|
||||
- comment:
|
||||
start: "###"
|
||||
end: "###"
|
||||
rules: []
|
||||
|
||||
- comment:
|
||||
start: "#"
|
||||
end: "$"
|
||||
rules:
|
||||
- todo: "(TODO|XXX|FIXME):?"
|
||||
- comment:
|
||||
start: "###"
|
||||
end: "###"
|
||||
rules:
|
||||
- todo: "(TODO|XXX|FIXME)"
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
filetype: conf
|
||||
|
||||
detect:
|
||||
filename: "\\.c[o]?nf$"
|
||||
|
||||
rules:
|
||||
- constant.string:
|
||||
start: "\""
|
||||
end: "\""
|
||||
skip: "\\\\."
|
||||
rules: []
|
||||
|
||||
- comment:
|
||||
start: "#"
|
||||
end: "$"
|
||||
rules: []
|
||||
|
||||
@@ -4,25 +4,36 @@ detect:
|
||||
filename: "(\\.c(c|pp|xx)$|\\.h(h|pp|xx)$|\\.ii?$|\\.(def)$)"
|
||||
|
||||
rules:
|
||||
- identifier: "\\b[A-Z_][0-9A-Z_]+\\b"
|
||||
- type: "\\b(auto|float|double|bool|char|int|short|long|sizeof|enum|void|static|const|constexpr|struct|union|typedef|extern|(un)?signed|inline)\\b"
|
||||
- type: "\\b((s?size)|((u_?)?int(8|16|32|64|ptr)))_t\\b"
|
||||
- statement: "\\b(class|namespace|template|public|protected|private|typename|this|friend|virtual|using|mutable|volatile|register|explicit)\\b"
|
||||
- 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))|char(8|16|32))_t|wchar_t)\\b"
|
||||
- type: "\\b(final|override)\\b"
|
||||
- type.keyword: "\\b(auto|volatile|const(expr|eval|init)?|mutable|register|thread_local|static|extern|decltype|explicit|virtual)\\b"
|
||||
- statement: "\\b(class|namespace|template|typename|this|friend|using|public|protected|private|noexcept)\\b"
|
||||
- statement: "\\b(concept|requires)\\b"
|
||||
- statement: "\\b(import|export|module)\\b"
|
||||
- statement: "\\b(for|if|while|do|else|case|default|switch)\\b"
|
||||
- statement: "\\b(try|throw|catch|operator|new|delete)\\b"
|
||||
- statement: "\\b(try|throw|catch|operator|new|delete|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)"
|
||||
- preproc: "^[[:space:]]*#[[:space:]]*(define|pragma|include|(un|ifn?)def|endif|el(if|se)|if|warning|error)|_Pragma"
|
||||
|
||||
# Conditionally-supported/extension keywords
|
||||
- statement: "\\b(asm|fortran)\\b"
|
||||
|
||||
# GCC builtins
|
||||
- statement: "(__attribute__[[:space:]]*\\(\\([^)]*\\)\\)|__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__)"
|
||||
|
||||
# Operator Color
|
||||
- symbol.operator: "([.:;,+*|=!\\%]|<|>|/|-|&)"
|
||||
- symbol.operator: "[-+*/%=<>.:;,~&|^!?]|\\b(sizeof|alignof|typeid|(and|or|xor|not)(_eq)?|bitor|compl|bitand|(const|dynamic|reinterpret|static)_cast)\\b"
|
||||
# Parenthetical Color
|
||||
- symbol.brackets: "[(){}]|\\[|\\]"
|
||||
|
||||
- constant.number: "(\\b[0-9]+\\b|\\b0x[0-9A-Fa-f]+\\b)"
|
||||
- constant.bool: "(\\b(true|false)\\b|NULL)"
|
||||
# Integer Literals
|
||||
- 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)"
|
||||
# Decimal Floating-point Literals
|
||||
- constant.number: "(\\b(([0-9']*[.][0-9']+|[0-9']+[.][0-9']*)([Ee][+-]?[0-9']+)?|[0-9']+[Ee][+-]?[0-9']+)[FfLl]?\\b)"
|
||||
# Hexadecimal Floating-point Literals
|
||||
- constant.number: "(\\b0[Xx]([0-9a-zA-Z']*[.][0-9a-zA-Z']+|[0-9a-zA-Z']+[.][0-9a-zA-Z']*)[Pp][+-]?[0-9']+[FfLl]?\\b)"
|
||||
- constant.bool: "(\\b(true|false|NULL|nullptr)\\b)"
|
||||
|
||||
- constant.string:
|
||||
start: "\""
|
||||
|
||||
@@ -5,10 +5,14 @@ detect:
|
||||
|
||||
rules:
|
||||
# Asciibetical list of reserved words
|
||||
- statement: "\\b(BEGIN|END|abstract|alias|and|begin|break|case|class|def|defined\\?|do|else|elsif|end|ensure|enum|false|for|fun|if|in|include|lib|loop|macro|module|next|nil|not|of|or|pointerof|private|protected|raise|redo|require|rescue|retry|return|self|sizeof|spawn|struct|super|then|true|type|undef|union|uninitialized|unless|until|when|while|yield)\\b"
|
||||
- statement: "\\b(abstract|alias|as|asm|begin|break|case|class|def|do|else|elsif|end|ensure|enum|extend|for|fun|if|in|include|instance_sizeof|lib|loop|macro|module|next|of|out|pointerof|private|protected|raise|require|rescue|return|select|self|sizeof|spawn|struct|super|then|type|typeof|uninitialized|union|unless|until|verbatim|when|while|with|yield)\\b"
|
||||
# Constants
|
||||
- constant: "(\\$|@|@@)?\\b[A-Z]+[0-9A-Z_a-z]*"
|
||||
- constant: "\\b(true|false|nil)\\b"
|
||||
- constant.number: "\\b[0-9]+\\b"
|
||||
# Ones that can't be in the same regex because they include non-words.
|
||||
# The nil? one has to be after the constants.
|
||||
- statement: "\\b(nil\\?|as(\\?|\\b)|is_a\\?|responds_to\\?)"
|
||||
- type: "(\\$|@|@@)?\\b[A-Z]+[0-9A-Z_a-z]*"
|
||||
# Crystal "symbols"
|
||||
- constant: "([ ]|^):[0-9A-Z_]+\\b"
|
||||
# Some unique things we want to stand out
|
||||
|
||||
19
runtime/syntax/gemini.yaml
Normal file
19
runtime/syntax/gemini.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
filetype: gemini
|
||||
|
||||
detect:
|
||||
filename: "\\.(gmi|gemini)$"
|
||||
|
||||
rules:
|
||||
# link lines
|
||||
- constant: "^=>[[:space:]].*"
|
||||
# preformatted text lines
|
||||
- special:
|
||||
start: "^```"
|
||||
end: "^```"
|
||||
rules: []
|
||||
# heading lines
|
||||
- special: "^#{1,3}.*"
|
||||
# unordered list items
|
||||
- identifier: "^\\*[[:space:]]"
|
||||
# quote lines
|
||||
- statement: "^>.*"
|
||||
113
runtime/syntax/groovy.yaml
Executable file
113
runtime/syntax/groovy.yaml
Executable file
@@ -0,0 +1,113 @@
|
||||
filetype: groovy
|
||||
|
||||
detect:
|
||||
filename: "\\.(groovy|gy|gvy|gsh|gradle)$"
|
||||
header: "^#!.*/(env +)?groovy *$"
|
||||
|
||||
rules:
|
||||
# And the style guide for constants is CONSTANT_CASE
|
||||
- identifier: "\\b[A-Z_$]+\\b"
|
||||
# The style guide for JVM languages is PascalCase for classes and interfaces
|
||||
- identifier.class: "\\b[A-Z][a-zA-Z0-9$]+\\b"
|
||||
|
||||
# Primitive types
|
||||
- type: "\\b(byte|short|int|long|float|double|char|boolean|void)\\b"
|
||||
|
||||
# Type-related keywords
|
||||
- type.keyword: "\\b(private|public|protected|static|final|var|def)\\b"
|
||||
|
||||
# Keywords
|
||||
- statement: "\\b(for|while|do|if|else|switch|case|default|try|catch|finally)\\b"
|
||||
- statement: "\\b(break|continue|return|throw|assert)\\b"
|
||||
- statement: "\\b(package|import|class|interface|trait|enum|extends|implements|throws)\\b"
|
||||
- statement: "\\b(this|super)\\b"
|
||||
# Unsused, but reserved keywords
|
||||
- statement: "\\b(goto|const)\\b"
|
||||
|
||||
# Operators and punctuation
|
||||
- symbol.operator: "[-+*/%=<>^~&|!?:;,.@]|\\b(in|is|as|instanceof|new)\\b"
|
||||
- symbol.brackets: "[(){}]|\\[|\\]"
|
||||
|
||||
# Decimal integer literal
|
||||
- constant.number: "(?i)\\b[1-9]([_0-9]*[0-9])?[GLIDF]?\\b"
|
||||
# Binary integer literal
|
||||
- constant.number: "(?i)\\b0b[01]([01_]*[01])?[GLIDF]?\\b"
|
||||
# Octal integer literal
|
||||
- constant.number: "(?i)\\b0[0-7]([0-7_]*[0-7])?[GLIDF]?\\b"
|
||||
# Hexadecimal integer literal
|
||||
- constant.number: "(?i)\\b0x[0-9a-f]([0-9a-f_]*[0-9a-f])?[GLIDF]?\\b"
|
||||
# Floating-point literal
|
||||
- constant.number: "(?i)\\b[0-9]([0-9_]*[0-9])?([.][0-9]([0-9_]*[0-9])?)?(e[+-]?[0-9]([0-9_]*[0-9])?)?[DF]?\\b"
|
||||
- constant.bool: "\\b(true|false|null)\\b"
|
||||
|
||||
# Annotations
|
||||
- identifier: "@[A-Za-z_$][A-Za-z0-9_$]*\\b"
|
||||
|
||||
# Single-quoted strings
|
||||
- constant.string:
|
||||
start: "'"
|
||||
end: "'"
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})"
|
||||
|
||||
# This also matches the Triple-double-quoted strings region, but I can't really find a way to mitigate it, all the while still matching "" as a string correctly
|
||||
# Also, nesting ${} are never going to be matched correctly with just regex either, so highlighting will break if one is to nest interpolation
|
||||
# These two problems combined mean slight mistakes in highlighing that the user is just going to have to deal with
|
||||
# Double-quoted strings
|
||||
- constant.string:
|
||||
start: "\""
|
||||
end: "\""
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})"
|
||||
- identifier.var: "\\x24[\\w\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+([.][a-zA-Z0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+)*"
|
||||
- identifier: "\\x24[{].*[}]"
|
||||
|
||||
# Triple-double-quoted strings
|
||||
- constant.string:
|
||||
start: "\"\"\""
|
||||
end: "\"\"\""
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})"
|
||||
- identifier.var: "\\x24[\\w\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+([.][a-zA-Z0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+)*"
|
||||
- identifier:
|
||||
start: "[$][{]"
|
||||
end: "[}]"
|
||||
rules: []
|
||||
|
||||
# Triple-single-quoted strings
|
||||
- constant.string:
|
||||
start: "'''"
|
||||
end: "'''"
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})"
|
||||
|
||||
# Slashy strings are left out, because they match in unwanted places pretty much all the time
|
||||
# Dollar-slashy strings
|
||||
- constant.string:
|
||||
start: "[$]/"
|
||||
end: "/[$]"
|
||||
rules: []
|
||||
|
||||
# Single-line comments
|
||||
- comment:
|
||||
start: "//"
|
||||
end: "$"
|
||||
rules:
|
||||
- todo: "(TODO|XXX|FIXME):?"
|
||||
|
||||
# Multiline comments
|
||||
- comment:
|
||||
start: "/[*]"
|
||||
end: "[*]/"
|
||||
rules:
|
||||
- todo: "(TODO|XXX|FIXME):?"
|
||||
|
||||
# Groovydoc comments
|
||||
- comment:
|
||||
start: "/[*][*]@?"
|
||||
end: "[*]/"
|
||||
rules: []
|
||||
@@ -4,7 +4,7 @@ detect:
|
||||
filename: "\\.lua$"
|
||||
|
||||
rules:
|
||||
- statement: "\\b(do|end|while|repeat|until|if|elseif|then|else|for|in|function|local|return)\\b"
|
||||
- statement: "\\b(do|end|while|break|repeat|until|if|elseif|then|else|for|in|function|local|return)\\b"
|
||||
- statement: "\\b(not|and|or)\\b"
|
||||
- statement: "\\b(debug|string|math|table|io|coroutine|os|utf8|bit32)\\b\\."
|
||||
- statement: "\\b(_ENV|_G|_VERSION|assert|collectgarbage|dofile|error|getfenv|getmetatable|ipairs|load|loadfile|module|next|pairs|pcall|print|rawequal|rawget|rawlen|rawset|require|select|setfenv|setmetatable|tonumber|tostring|type|unpack|xpcall)\\s*\\("
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
filetype: patch
|
||||
|
||||
detect:
|
||||
detect:
|
||||
filename: "\\.(patch|diff)$"
|
||||
header: "^diff"
|
||||
|
||||
rules:
|
||||
- brightgreen: "^\\+.*"
|
||||
|
||||
@@ -26,7 +26,7 @@ rules:
|
||||
# parentheses
|
||||
- symbol.brackets: "([(){}]|\\[|\\])"
|
||||
# numbers
|
||||
- constant.number: "\\b[1-9](_?[0-9])*(\\.([0-9](_?[0-9])*)?)?(e[0-9](_?[0-9])*)?\\b" # decimal
|
||||
- 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
|
||||
@@ -58,5 +58,5 @@ rules:
|
||||
- comment:
|
||||
start: "#"
|
||||
end: "$"
|
||||
rules:
|
||||
- todo: "(TODO|FIXME):?"
|
||||
rules: # AKA Code tags (PEP 350)
|
||||
- todo: "(TODO|FIXME|HACK|BUG|NOTE|FAQ|MNEMONIC|REQ|RFE|IDEA|PORT|\\?\\?\\?|!!!|GLOSS|SEE|TODOC|STAT|RVD|CRED):?"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
filetype: perl6
|
||||
filetype: raku
|
||||
|
||||
detect:
|
||||
filename: "(\\.p6$|\\.pl6$|\\.pm6$)"
|
||||
filename: "(\\.p6$|\\.pl6$|\\.pm6$|\\.raku$|\\.rakumod$|\\.rakudoc$)"
|
||||
|
||||
rules:
|
||||
- type: "\\b(accept|alarm|atan2|bin(d|mode)|c(aller|h(dir|mod|op|own|root)|lose(dir)?|onnect|os|rypt)|d(bm(close|open)|efined|elete|ie|o|ump)|e(ach|of|val|x(ec|ists|it|p))|f(cntl|ileno|lock|ork)|get(c|login|peername|pgrp|ppid|priority|pwnam|(host|net|proto|serv)byname|pwuid|grgid|(host|net)byaddr|protobynumber|servbyport)|([gs]et|end)(pw|gr|host|net|proto|serv)ent|getsock(name|opt)|gmtime|goto|grep|hex|index|int|ioctl|join|keys|kill|last|length|link|listen|local(time)?|log|lstat|m|mkdir|msg(ctl|get|snd|rcv)|next|oct|open(dir)?|ord|pack|pipe|pop|printf?|push|q|qq|qx|rand|re(ad(dir|link)?|cv|do|name|quire|set|turn|verse|winddir)|rindex|rmdir|s|scalar|seek|seekdir|se(lect|mctl|mget|mop|nd|tpgrp|tpriority|tsockopt)|shift|shm(ctl|get|read|write)|shutdown|sin|sleep|socket(pair)?|sort|spli(ce|t)|sprintf|sqrt|srand|stat|study|substr|symlink|sys(call|read|tem|write)|tell(dir)?|time|tr|y|truncate|umask|un(def|link|pack|shift)|utime|values|vec|wait(pid)?|wantarray|warn|write)\\b"
|
||||
@@ -7,6 +7,10 @@ rules:
|
||||
- type: "\\b(boolean|byte|char|double|float|int|long|new|short|this|transient|void)\\b"
|
||||
- statement: "\\b(match|val|var|break|case|catch|continue|default|do|else|finally|for|if|return|switch|throw|try|while)\\b"
|
||||
- statement: "\\b(def|object|case|trait|lazy|implicit|abstract|class|extends|with|final|implements|override|import|instanceof|interface|native|package|private|protected|public|static|strictfp|super|synchronized|throws|volatile|sealed)\\b"
|
||||
- constant.string:
|
||||
start: "\"\"\""
|
||||
end: "\"\"\""
|
||||
rules: []
|
||||
- constant.string:
|
||||
start: "\""
|
||||
end: "\""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
filetype: typescript
|
||||
|
||||
detect:
|
||||
filename: "\\.ts$"
|
||||
filename: "\\.tsx?$"
|
||||
|
||||
rules:
|
||||
- constant.number: "\\b[-+]?([1-9][0-9]*|0[0-7]*|0x[0-9a-fA-F]+)([uU][lL]?|[lL][uU]?)?\\b"
|
||||
|
||||
@@ -7,12 +7,46 @@ rules:
|
||||
- default:
|
||||
start: "<template.*?>"
|
||||
end: "</template.*?>"
|
||||
rules:
|
||||
- include: "html5"
|
||||
limit-group: symbol.tag
|
||||
rules:
|
||||
- error: "<[^!].*?>"
|
||||
- symbol.tag: "(?i)<[/]?(a|a(bbr|ddress|rea|rticle|side|udio)|b|b(ase|d(i|o)|lockquote|r|utton)|ca(nvas|ption)|center|cite|co(de|l|lgroup)|d(ata|atalist|d|el|etails|fn|ialog|l|t)|em|embed|fieldset|fig(caption|ure)|form|iframe|h[1-6]|hr|i|img|in(put|s)|kbd|keygen|label|legend|li|link|ma(in|p|rk)|menu|menuitem|met(a|er)|nav|noscript|o(bject|l|pt(group|ion)|utput)|p|param|picture|pre|progress|q|r(p|t|uby)|s|samp|se(ction|lect)|svg|small|source|span|strong|su(b|p|mmary)|textarea|time|track|u|ul|var|video|wbr)( .*)*?>"
|
||||
- symbol.tag.extended: "(?i)<[/]?(body|div|html|head(er)?|footer|title|table|t(body|d|h(ead)?|r|foot))( .*)*?>"
|
||||
- preproc: "(?i)<[/]?(script|style)( .*)*?>"
|
||||
- special: "&[^;[[:space:]]]*;"
|
||||
|
||||
- identifier: "(alt|bgcolor|class|height|href|id|label|longdesc|name|on(click|focus|load|mouseover)|placeholder|size|span|src|style|target|type|value|width)="
|
||||
- symbol: "[:=]"
|
||||
- constant.string: "\"[^\"]*\""
|
||||
- constant.number: "(?i)#[0-9A-F]{6,6}"
|
||||
|
||||
- symbol.tag: "<|>"
|
||||
- constant.string.url: "(ftp(s)?|http(s)?|git|chrome)://[^ ]+"
|
||||
- comment: "<!--.+?-->"
|
||||
#- preproc: "<!DOCTYPE.+?>"
|
||||
- comment.block:
|
||||
start: "<!\\-\\-"
|
||||
end: "\\-\\->"
|
||||
rules: []
|
||||
|
||||
# Bootstrap
|
||||
- symbol.tag.extended: "(?i)<[/]?(b-alert|b-aspect|b-avatar|b-badge|b-icon|b-breadcrumb|b-button-group|b-button-toolbar|b-button|b-calendar|b-card-text|b-card-input|b-card|b-carousel-slide|b-carousel|b-collapse|b-dropdown|b-dropdown-item|b-dropdown-divider|b-embed|b-form-checkbox-group|b-form-checkbox|b-form-datepicker|b-form-file|b-form-group|b-form-input|b-form-radio|b-form-rating|b-form-select|b-form-spinbutton|b-form-tags|b-form-textarea|b-form|b-form-timepicker|b-img-lazy|b-img|b-input-group|b-jumbotron|b-input|b-container|b-row|b-col|b-link|b-list-group|b-list-group-item|b-media|b-modal|b-nav|b-nav-item|b-nav-item-dropdown|b-nav-text|b-nav-form|b-navbar|b-navbar-brand|b-navbar-toggle|b-navbar-nav|b-overlay|b-pagination|b-pagination-nav|b-popover|b-progress|b-progress-bar|b-sidebar|b-skeleton-wrapper|b-skeleton|b-spinner|b-table|b-table-lite|b-table-simple|b-tabs|b-tab|b-time|b-toast|b-tooltip)\\b"
|
||||
- identifier: "(variant|title|show|shadow|icon|align-h|align-v|label-for|@submit|tag|img-alt|img-src|data-toggle|data-target|aria-controls|aria-expanded|aria-label|aria-disabled|tabindex|:interval|background|img-width|img-height|@sliding-start|@sliding-end|cols|header|@reset)="
|
||||
- symbol: "[:=]"
|
||||
# Vue
|
||||
- symbol.tag.extended: "(?i)<[/]?(component|transition|transition-group|keep-alive|slot)\\b"
|
||||
- identifier: "(v-text|v-html|v-show|v-if|v-else|v-else-if|v-for|v-on|v-bind|v-model|v-slot|v-pre|v-cloak|v-once|key|ref|is|@click)="
|
||||
- symbol: "[:=]"
|
||||
# Vue-router
|
||||
- symbol.tag.extended: "(?i)<[/]?(router-link|router-view)\\b"
|
||||
- identifier: "(to|v-slot)="
|
||||
- symbol: "[:=]"
|
||||
|
||||
|
||||
- default:
|
||||
start: "<script>"
|
||||
end: "</script>"
|
||||
limit-group: symbol.tag
|
||||
rules:
|
||||
- include: "javascript"
|
||||
|
||||
@@ -25,6 +59,9 @@ rules:
|
||||
- default:
|
||||
start: "<style.*?>"
|
||||
end: "</style.*?>"
|
||||
limit-group: symbol.tag
|
||||
rules:
|
||||
- include: "css"
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user