mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-31 23:27:10 +09:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1856891622 | ||
|
|
8a250f7d95 | ||
|
|
7a013f666e | ||
|
|
41a24e61d6 | ||
|
|
d953339a56 | ||
|
|
76e1d7a3a7 | ||
|
|
91b65001c9 | ||
|
|
aa74b1233c | ||
|
|
61baa73d70 | ||
|
|
efe343b37c | ||
|
|
cc8e9a7e06 | ||
|
|
d7f7d845b9 | ||
|
|
8f0418c9a8 | ||
|
|
71af765b4e | ||
|
|
c0f279ffe8 | ||
|
|
ae9bb763fb |
1
Makefile
1
Makefile
@@ -41,6 +41,7 @@ runtime:
|
||||
go get -u github.com/jteeuwen/go-bindata/...
|
||||
$(GOBIN)/go-bindata -nometadata -o runtime.go runtime/...
|
||||
mv runtime.go cmd/micro
|
||||
gofmt -w cmd/micro/runtime.go
|
||||
|
||||
test:
|
||||
go test ./cmd/micro
|
||||
|
||||
@@ -1515,6 +1515,42 @@ func (v *View) PageDown(usePlugin bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SelectPageUp selects up one page
|
||||
func (v *View) SelectPageUp(usePlugin bool) bool {
|
||||
if usePlugin && !PreActionCall("SelectPageUp", v) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Cursor.HasSelection() {
|
||||
v.Cursor.OrigSelection[0] = v.Cursor.Loc
|
||||
}
|
||||
v.Cursor.UpN(v.Height)
|
||||
v.Cursor.SelectTo(v.Cursor.Loc)
|
||||
|
||||
if usePlugin {
|
||||
return PostActionCall("SelectPageUp", v)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SelectPageDown selects down one page
|
||||
func (v *View) SelectPageDown(usePlugin bool) bool {
|
||||
if usePlugin && !PreActionCall("SelectPageDown", v) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Cursor.HasSelection() {
|
||||
v.Cursor.OrigSelection[0] = v.Cursor.Loc
|
||||
}
|
||||
v.Cursor.DownN(v.Height)
|
||||
v.Cursor.SelectTo(v.Cursor.Loc)
|
||||
|
||||
if usePlugin {
|
||||
return PostActionCall("SelectPageDown", v)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CursorPageUp places the cursor a page up
|
||||
func (v *View) CursorPageUp(usePlugin bool) bool {
|
||||
if usePlugin && !PreActionCall("CursorPageUp", v) {
|
||||
|
||||
@@ -80,6 +80,8 @@ var bindingActions = map[string]func(*View, bool) bool{
|
||||
"End": (*View).End,
|
||||
"PageUp": (*View).PageUp,
|
||||
"PageDown": (*View).PageDown,
|
||||
"SelectPageUp": (*View).SelectPageUp,
|
||||
"SelectPageDown": (*View).SelectPageDown,
|
||||
"HalfPageUp": (*View).HalfPageUp,
|
||||
"HalfPageDown": (*View).HalfPageDown,
|
||||
"StartOfLine": (*View).StartOfLine,
|
||||
@@ -255,6 +257,7 @@ var bindingKeys = map[string]tcell.Key{
|
||||
"Escape": tcell.KeyEscape,
|
||||
"Enter": tcell.KeyEnter,
|
||||
"Backspace": tcell.KeyBackspace2,
|
||||
"OldBackspace": tcell.KeyBackspace,
|
||||
|
||||
// I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
|
||||
"PgUp": tcell.KeyPgUp,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/gob"
|
||||
@@ -11,15 +12,17 @@ import (
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/zyedidia/micro/cmd/micro/highlight"
|
||||
)
|
||||
|
||||
const LargeFileThreshold = 50000
|
||||
|
||||
var (
|
||||
// 0 - no line type detected
|
||||
// 1 - lf detected
|
||||
@@ -60,7 +63,7 @@ type Buffer struct {
|
||||
highlighter *highlight.Highlighter
|
||||
|
||||
// Hash of the original buffer -- empty if fastdirty is on
|
||||
origHash [16]byte
|
||||
origHash [md5.Size]byte
|
||||
|
||||
// Buffer local settings
|
||||
Settings map[string]interface{}
|
||||
@@ -74,12 +77,12 @@ type SerializedBuffer struct {
|
||||
ModTime time.Time
|
||||
}
|
||||
|
||||
// NewBufferFromFile opens a new buffer using the given filepath
|
||||
// NewBufferFromFile opens a new buffer using the given path
|
||||
// It will also automatically handle `~`, and line/column with filename:l:c
|
||||
// It will return an empty buffer if the filepath does not exist
|
||||
// It will return an empty buffer if the path does not exist
|
||||
// and an error if the file is a directory
|
||||
func NewBufferFromFile(path string) (*Buffer, error) {
|
||||
filename := GetPath(path)
|
||||
filename, cursorPosition := GetPathAndCursorPosition(path)
|
||||
filename = ReplaceHome(filename)
|
||||
file, err := os.Open(filename)
|
||||
fileInfo, _ := os.Stat(filename)
|
||||
@@ -93,42 +96,22 @@ func NewBufferFromFile(path string) (*Buffer, error) {
|
||||
var buf *Buffer
|
||||
if err != nil {
|
||||
// File does not exist -- create an empty buffer with that name
|
||||
buf = NewBufferFromString("", path)
|
||||
buf = NewBufferFromString("", filename)
|
||||
} else {
|
||||
buf = NewBuffer(file, FSize(file), path)
|
||||
buf = NewBuffer(file, FSize(file), filename, cursorPosition)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// NewBufferFromString creates a new buffer containing the given
|
||||
// string
|
||||
// NewBufferFromString creates a new buffer containing the given string
|
||||
func NewBufferFromString(text, path string) *Buffer {
|
||||
return NewBuffer(strings.NewReader(text), int64(len(text)), path)
|
||||
return NewBuffer(strings.NewReader(text), int64(len(text)), path, nil)
|
||||
}
|
||||
|
||||
// NewBuffer creates a new buffer from a given reader with a given path
|
||||
func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
|
||||
startpos := Loc{0, 0}
|
||||
startposErr := true
|
||||
if strings.Contains(path, ":") {
|
||||
var err error
|
||||
split := strings.Split(path, ":")
|
||||
path = split[0]
|
||||
startpos.Y, err = strconv.Atoi(split[1])
|
||||
if err != nil {
|
||||
messenger.Error("Error opening file: ", err)
|
||||
} else {
|
||||
startposErr = false
|
||||
if len(split) > 2 {
|
||||
startpos.X, err = strconv.Atoi(split[2])
|
||||
if err != nil {
|
||||
messenger.Error("Error opening file: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewBuffer(reader io.Reader, size int64, path string, cursorPosition []string) *Buffer {
|
||||
// check if the file is already open in a tab. If it's open return the buffer to that tab
|
||||
if path != "" {
|
||||
for _, tab := range tabs {
|
||||
for _, view := range tab.Views {
|
||||
@@ -172,51 +155,15 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
|
||||
os.Mkdir(configDir+"/buffers/", os.ModePerm)
|
||||
}
|
||||
|
||||
// Put the cursor at the first spot
|
||||
cursorStartX := 0
|
||||
cursorStartY := 0
|
||||
// If -startpos LINE,COL was passed, use start position LINE,COL
|
||||
if len(*flagStartPos) > 0 || !startposErr {
|
||||
positions := strings.Split(*flagStartPos, ",")
|
||||
if len(positions) == 2 || !startposErr {
|
||||
var lineNum, colNum int
|
||||
var errPos1, errPos2 error
|
||||
if !startposErr {
|
||||
lineNum = startpos.Y
|
||||
colNum = startpos.X
|
||||
} else {
|
||||
lineNum, errPos1 = strconv.Atoi(positions[0])
|
||||
colNum, errPos2 = strconv.Atoi(positions[1])
|
||||
}
|
||||
if errPos1 == nil && errPos2 == nil {
|
||||
cursorStartX = colNum
|
||||
cursorStartY = lineNum - 1
|
||||
// Check to avoid line overflow
|
||||
if cursorStartY > b.NumLines {
|
||||
cursorStartY = b.NumLines - 1
|
||||
} else if cursorStartY < 0 {
|
||||
cursorStartY = 0
|
||||
}
|
||||
// Check to avoid column overflow
|
||||
if cursorStartX > len(b.Line(cursorStartY)) {
|
||||
cursorStartX = len(b.Line(cursorStartY))
|
||||
} else if cursorStartX < 0 {
|
||||
cursorStartX = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cursorLocation, cursorLocationError := GetBufferCursorLocation(cursorPosition, b)
|
||||
b.Cursor = Cursor{
|
||||
Loc: Loc{
|
||||
X: cursorStartX,
|
||||
Y: cursorStartY,
|
||||
},
|
||||
Loc: cursorLocation,
|
||||
buf: b,
|
||||
}
|
||||
|
||||
InitLocalSettings(b)
|
||||
|
||||
if startposErr && len(*flagStartPos) == 0 && (b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool)) {
|
||||
if cursorLocationError != nil && len(*flagStartPos) == 0 && (b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool)) {
|
||||
// If either savecursor or saveundo is turned on, we need to load the serialized information
|
||||
// from ~/.config/micro/buffers
|
||||
file, err := os.Open(configDir + "/buffers/" + EscapePath(b.AbsPath))
|
||||
@@ -246,11 +193,11 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
|
||||
}
|
||||
|
||||
if !b.Settings["fastdirty"].(bool) {
|
||||
if size > 50000 {
|
||||
if size > LargeFileThreshold {
|
||||
// If the file is larger than a megabyte fastdirty needs to be on
|
||||
b.Settings["fastdirty"] = true
|
||||
} else {
|
||||
b.origHash = md5.Sum([]byte(b.String()))
|
||||
calcHash(b, &b.origHash)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,6 +206,52 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
|
||||
return b
|
||||
}
|
||||
|
||||
func GetBufferCursorLocation(cursorPosition []string, b *Buffer) (Loc, error) {
|
||||
// parse the cursor position. The cursor location is ALWAYS initialised to 0, 0 even when
|
||||
// an error occurs due to lack of arguments or because the arguments are not numbers
|
||||
cursorLocation, cursorLocationError := ParseCursorLocation(cursorPosition)
|
||||
|
||||
// Put the cursor at the first spot. In the logic for cursor position the -startpos
|
||||
// flag is processed first and will overwrite any line/col parameters with colons after the filename
|
||||
if len(*flagStartPos) > 0 || cursorLocationError == nil {
|
||||
var lineNum, colNum int
|
||||
var errPos1, errPos2 error
|
||||
|
||||
positions := strings.Split(*flagStartPos, ",")
|
||||
|
||||
// if the -startpos flag contains enough args use them for the cursor location.
|
||||
// In this case args passed at the end of the filename will be ignored
|
||||
if len(positions) == 2 {
|
||||
lineNum, errPos1 = strconv.Atoi(positions[0])
|
||||
colNum, errPos2 = strconv.Atoi(positions[1])
|
||||
}
|
||||
// if -startpos has invalid arguments, use the arguments from filename.
|
||||
// This will have a default value (0, 0) even when the filename arguments are invalid
|
||||
if errPos1 != nil || errPos2 != nil || len(*flagStartPos) == 0 {
|
||||
// otherwise check if there are any arguments after the filename and use them
|
||||
lineNum = cursorLocation.Y
|
||||
colNum = cursorLocation.X
|
||||
}
|
||||
|
||||
// if some arguments were found make sure they don't go outside the file and cause overflows
|
||||
cursorLocation.Y = lineNum - 1
|
||||
cursorLocation.X = colNum
|
||||
// Check to avoid line overflow
|
||||
if cursorLocation.Y > b.NumLines-1 {
|
||||
cursorLocation.Y = b.NumLines - 1
|
||||
} else if cursorLocation.Y < 0 {
|
||||
cursorLocation.Y = 0
|
||||
}
|
||||
// Check to avoid column overflow
|
||||
if cursorLocation.X > len(b.Line(cursorLocation.Y)) {
|
||||
cursorLocation.X = len(b.Line(cursorLocation.Y))
|
||||
} else if cursorLocation.X < 0 {
|
||||
cursorLocation.X = 0
|
||||
}
|
||||
}
|
||||
return cursorLocation, cursorLocationError
|
||||
}
|
||||
|
||||
// GetName returns the name that should be displayed in the statusline
|
||||
// for this buffer
|
||||
func (b *Buffer) GetName() string {
|
||||
@@ -440,38 +433,41 @@ func (b *Buffer) SaveWithSudo() error {
|
||||
|
||||
// Serialize serializes the buffer to configDir/buffers
|
||||
func (b *Buffer) Serialize() error {
|
||||
if b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool) {
|
||||
file, err := os.Create(configDir + "/buffers/" + EscapePath(b.AbsPath))
|
||||
if err == nil {
|
||||
enc := gob.NewEncoder(file)
|
||||
gob.Register(TextEvent{})
|
||||
err = enc.Encode(SerializedBuffer{
|
||||
b.EventHandler,
|
||||
b.Cursor,
|
||||
b.ModTime,
|
||||
})
|
||||
}
|
||||
err = file.Close()
|
||||
return err
|
||||
if !b.Settings["savecursor"].(bool) && !b.Settings["saveundo"].(bool) {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
name := configDir + "/buffers/" + EscapePath(b.AbsPath)
|
||||
|
||||
return overwriteFile(name, func(file io.Writer) error {
|
||||
return gob.NewEncoder(file).Encode(SerializedBuffer{
|
||||
b.EventHandler,
|
||||
b.Cursor,
|
||||
b.ModTime,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
gob.Register(TextEvent{})
|
||||
gob.Register(SerializedBuffer{})
|
||||
}
|
||||
|
||||
// SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
|
||||
func (b *Buffer) SaveAs(filename string) error {
|
||||
b.UpdateRules()
|
||||
if b.Settings["rmtrailingws"].(bool) {
|
||||
r, _ := regexp.Compile(`[ \t]+$`)
|
||||
for lineNum, line := range b.Lines(0, b.NumLines) {
|
||||
indices := r.FindStringIndex(line)
|
||||
if indices == nil {
|
||||
continue
|
||||
for i, l := range b.lines {
|
||||
pos := len(bytes.TrimRightFunc(l.data, unicode.IsSpace))
|
||||
|
||||
if pos < len(l.data) {
|
||||
b.deleteToEnd(Loc{pos, i})
|
||||
}
|
||||
startLoc := Loc{indices[0], lineNum}
|
||||
b.deleteToEnd(startLoc)
|
||||
}
|
||||
|
||||
b.Cursor.Relocate()
|
||||
}
|
||||
|
||||
if b.Settings["eofnewline"].(bool) {
|
||||
end := b.End()
|
||||
if b.RuneAt(Loc{end.X - 1, end.Y}) != '\n' {
|
||||
@@ -484,7 +480,7 @@ func (b *Buffer) SaveAs(filename string) error {
|
||||
}()
|
||||
|
||||
// Removes any tilde and replaces with the absolute path to home
|
||||
var absFilename string = ReplaceHome(filename)
|
||||
absFilename := ReplaceHome(filename)
|
||||
|
||||
// Get the leading path to the file | "." is returned if there's no leading path provided
|
||||
if dirname := filepath.Dir(absFilename); dirname != "." {
|
||||
@@ -504,42 +500,52 @@ func (b *Buffer) SaveAs(filename string) error {
|
||||
}
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(absFilename, os.O_WRONLY|os.O_CREATE, 0644)
|
||||
defer f.Close()
|
||||
var fileSize int
|
||||
|
||||
err := overwriteFile(absFilename, func(file io.Writer) (e error) {
|
||||
if len(b.lines) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// end of line
|
||||
var eol []byte
|
||||
|
||||
if b.Settings["fileformat"] == "dos" {
|
||||
eol = []byte{'\r', '\n'}
|
||||
} else {
|
||||
eol = []byte{'\n'}
|
||||
}
|
||||
|
||||
// write lines
|
||||
if fileSize, e = file.Write(b.lines[0].data); e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, l := range b.lines[1:] {
|
||||
if _, e = file.Write(eol); e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, e = file.Write(l.data); e != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fileSize += len(eol) + len(l.data)
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.Truncate(0); err != nil {
|
||||
return err
|
||||
}
|
||||
useCrlf := b.Settings["fileformat"] == "dos"
|
||||
size := 0
|
||||
for i, l := range b.lines {
|
||||
size += len(l.data)
|
||||
if _, err := f.Write(l.data); err != nil {
|
||||
return err
|
||||
}
|
||||
if i != len(b.lines)-1 {
|
||||
if useCrlf {
|
||||
size += 2
|
||||
if _, err := f.Write([]byte{'\r', '\n'}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
size++
|
||||
if _, err := f.Write([]byte{'\n'}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !b.Settings["fastdirty"].(bool) {
|
||||
if size > 50000 {
|
||||
// If the file is larger than a megabyte fastdirty needs to be on
|
||||
if fileSize > LargeFileThreshold {
|
||||
// For large files 'fastdirty' needs to be on
|
||||
b.Settings["fastdirty"] = true
|
||||
} else {
|
||||
b.origHash = md5.Sum([]byte(b.String()))
|
||||
calcHash(b, &b.origHash)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,6 +554,48 @@ func (b *Buffer) SaveAs(filename string) error {
|
||||
return b.Serialize()
|
||||
}
|
||||
|
||||
// overwriteFile opens the given file for writing, truncating if one exists, and then calls
|
||||
// the supplied function with the file as io.Writer object, also making sure the file is
|
||||
// closed afterwards.
|
||||
func overwriteFile(name string, fn func(io.Writer) error) (err error) {
|
||||
var file *os.File
|
||||
|
||||
if file, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if e := file.Close(); e != nil && err == nil {
|
||||
err = e
|
||||
}
|
||||
}()
|
||||
|
||||
w := bufio.NewWriter(file)
|
||||
|
||||
if err = fn(w); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = w.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
// calcHash calculates md5 hash of all lines in the buffer
|
||||
func calcHash(b *Buffer, out *[md5.Size]byte) {
|
||||
h := md5.New()
|
||||
|
||||
if len(b.lines) > 0 {
|
||||
h.Write(b.lines[0].data)
|
||||
|
||||
for _, l := range b.lines[1:] {
|
||||
h.Write([]byte{'\n'})
|
||||
h.Write(l.data)
|
||||
}
|
||||
}
|
||||
|
||||
h.Sum((*out)[:0])
|
||||
}
|
||||
|
||||
// SaveAsWithSudo is the same as SaveAs except it uses a neat trick
|
||||
// with tee to use sudo so the user doesn't have to reopen micro with sudo
|
||||
func (b *Buffer) SaveAsWithSudo(filename string) error {
|
||||
@@ -592,7 +640,11 @@ func (b *Buffer) Modified() bool {
|
||||
if b.Settings["fastdirty"].(bool) {
|
||||
return b.IsModified
|
||||
}
|
||||
return b.origHash != md5.Sum([]byte(b.String()))
|
||||
|
||||
var buff [md5.Size]byte
|
||||
|
||||
calcHash(b, &buff)
|
||||
return buff != b.origHash
|
||||
}
|
||||
|
||||
func (b *Buffer) insert(pos Loc, value []byte) {
|
||||
@@ -631,7 +683,7 @@ func (b *Buffer) RuneAt(loc Loc) rune {
|
||||
return '\n'
|
||||
}
|
||||
|
||||
// Line returns a single line as an array of runes
|
||||
// LineBytes returns a single line as an array of runes
|
||||
func (b *Buffer) LineBytes(n int) []byte {
|
||||
if n >= len(b.lines) {
|
||||
return []byte{}
|
||||
@@ -639,7 +691,7 @@ func (b *Buffer) LineBytes(n int) []byte {
|
||||
return b.lines[n].data
|
||||
}
|
||||
|
||||
// Line returns a single line as an array of runes
|
||||
// LineRunes returns a single line as an array of runes
|
||||
func (b *Buffer) LineRunes(n int) []rune {
|
||||
if n >= len(b.lines) {
|
||||
return []rune{}
|
||||
@@ -671,8 +723,16 @@ func (b *Buffer) Lines(start, end int) []string {
|
||||
}
|
||||
|
||||
// Len gives the length of the buffer
|
||||
func (b *Buffer) Len() int {
|
||||
return Count(b.String())
|
||||
func (b *Buffer) Len() (n int) {
|
||||
for _, l := range b.lines {
|
||||
n += utf8.RuneCount(l.data)
|
||||
}
|
||||
|
||||
if len(b.lines) > 1 {
|
||||
n += len(b.lines) - 1 // account for newlines
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MoveLinesUp moves the range of lines up one row
|
||||
|
||||
117
cmd/micro/buffer_test.go
Normal file
117
cmd/micro/buffer_test.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetBufferCursorLocationEmptyArgs(t *testing.T) {
|
||||
buf := NewBufferFromString("this is my\nbuffer\nfile\nhello", "")
|
||||
|
||||
location, err := GetBufferCursorLocation(nil, buf)
|
||||
|
||||
assertEqual(t, 0, location.Y)
|
||||
assertEqual(t, 0, location.X)
|
||||
|
||||
// an error is present due to the cursorLocation being nil
|
||||
assertTrue(t, err != nil)
|
||||
|
||||
}
|
||||
|
||||
func TestGetBufferCursorLocationStartposFlag(t *testing.T) {
|
||||
buf := NewBufferFromString("this is my\nbuffer\nfile\nhello", "")
|
||||
|
||||
*flagStartPos = "1,2"
|
||||
|
||||
location, err := GetBufferCursorLocation(nil, buf)
|
||||
|
||||
// note: 1 is subtracted from the line to get the correct index in the buffer
|
||||
assertTrue(t, 0 == location.Y)
|
||||
assertTrue(t, 2 == location.X)
|
||||
|
||||
// an error is present due to the cursorLocation being nil
|
||||
assertTrue(t, err != nil)
|
||||
}
|
||||
|
||||
func TestGetBufferCursorLocationInvalidStartposFlag(t *testing.T) {
|
||||
buf := NewBufferFromString("this is my\nbuffer\nfile\nhello", "")
|
||||
|
||||
*flagStartPos = "apples,2"
|
||||
|
||||
location, err := GetBufferCursorLocation(nil, buf)
|
||||
// expect to default to the start of the file, which is 0,0
|
||||
assertEqual(t, 0, location.Y)
|
||||
assertEqual(t, 0, location.X)
|
||||
|
||||
// an error is present due to the cursorLocation being nil
|
||||
assertTrue(t, err != nil)
|
||||
}
|
||||
func TestGetBufferCursorLocationStartposFlagAndCursorPosition(t *testing.T) {
|
||||
text := "this is my\nbuffer\nfile\nhello"
|
||||
cursorPosition := []string{"3", "1"}
|
||||
|
||||
buf := NewBufferFromString(text, "")
|
||||
|
||||
*flagStartPos = "1,2"
|
||||
|
||||
location, err := GetBufferCursorLocation(cursorPosition, buf)
|
||||
// expect to have the flag positions, not the cursor position
|
||||
// note: 1 is subtracted from the line to get the correct index in the buffer
|
||||
assertEqual(t, 0, location.Y)
|
||||
assertEqual(t, 2, location.X)
|
||||
|
||||
assertTrue(t, err == nil)
|
||||
}
|
||||
func TestGetBufferCursorLocationCursorPositionAndInvalidStartposFlag(t *testing.T) {
|
||||
text := "this is my\nbuffer\nfile\nhello"
|
||||
cursorPosition := []string{"3", "1"}
|
||||
|
||||
buf := NewBufferFromString(text, "")
|
||||
|
||||
*flagStartPos = "apples,2"
|
||||
|
||||
location, err := GetBufferCursorLocation(cursorPosition, buf)
|
||||
// expect to have the flag positions, not the cursor position
|
||||
// note: 1 is subtracted from the line to get the correct index in the buffer
|
||||
assertEqual(t, 2, location.Y)
|
||||
assertEqual(t, 1, location.X)
|
||||
|
||||
// no errors this time as cursorPosition is not nil
|
||||
assertTrue(t, err == nil)
|
||||
}
|
||||
|
||||
func TestGetBufferCursorLocationNoErrorWhenOverflowWithStartpos(t *testing.T) {
|
||||
text := "this is my\nbuffer\nfile\nhello"
|
||||
|
||||
buf := NewBufferFromString(text, "")
|
||||
|
||||
*flagStartPos = "50,50"
|
||||
|
||||
location, err := GetBufferCursorLocation(nil, buf)
|
||||
// expect to have the flag positions, not the cursor position
|
||||
assertEqual(t, buf.NumLines-1, location.Y)
|
||||
assertEqual(t, 5, location.X)
|
||||
|
||||
// error is expected as cursorPosition is nil
|
||||
assertTrue(t, err != nil)
|
||||
}
|
||||
func TestGetBufferCursorLocationNoErrorWhenOverflowWithCursorPosition(t *testing.T) {
|
||||
text := "this is my\nbuffer\nfile\nhello"
|
||||
cursorPosition := []string{"50", "2"}
|
||||
|
||||
*flagStartPos = ""
|
||||
|
||||
buf := NewBufferFromString(text, "")
|
||||
|
||||
location, err := GetBufferCursorLocation(cursorPosition, buf)
|
||||
// expect to have the flag positions, not the cursor position
|
||||
assertEqual(t, buf.NumLines-1, location.Y)
|
||||
assertEqual(t, 2, location.X)
|
||||
|
||||
// error is expected as cursorPosition is nil
|
||||
assertTrue(t, err == nil)
|
||||
}
|
||||
|
||||
//func TestGetBufferCursorLocationColonArgs(t *testing.T) {
|
||||
// buf := new(Buffer)
|
||||
|
||||
//}
|
||||
@@ -76,8 +76,8 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
|
||||
curX := buf.Cursor.X
|
||||
curLoc := buf.Cursor.Loc
|
||||
if buf.Settings["matchbraceleft"].(bool) {
|
||||
curX--
|
||||
if curX > 0 {
|
||||
curX--
|
||||
curLoc = curLoc.Move(-1, buf)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ func (c *Cursor) Start() {
|
||||
c.LastVisualX = c.GetVisualX()
|
||||
}
|
||||
|
||||
// StartOfText moves the cursor to the first non-whitespace rune of
|
||||
// StartOfText moves the cursor to the first non-whitespace rune of
|
||||
// the line it is on
|
||||
func (c *Cursor) StartOfText() {
|
||||
c.Start()
|
||||
|
||||
@@ -176,17 +176,7 @@ func InitConfigDir() {
|
||||
// InitScreen creates and initializes the tcell screen
|
||||
func InitScreen() {
|
||||
// Should we enable true color?
|
||||
truecolor := false
|
||||
colorterm := os.Getenv("COLORTERM")
|
||||
if colorterm == "24bit" || colorterm == "truecolor" {
|
||||
truecolor = true
|
||||
}
|
||||
microtc := os.Getenv("MICRO_TRUECOLOR")
|
||||
if microtc == "1" {
|
||||
truecolor = true
|
||||
} else if microtc == "0" {
|
||||
truecolor = false
|
||||
}
|
||||
truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"
|
||||
|
||||
tcelldb := os.Getenv("TCELLDB")
|
||||
os.Setenv("TCELLDB", configDir+"/.tcelldb")
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,11 +4,13 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/flynn/json5"
|
||||
"github.com/zyedidia/glob"
|
||||
@@ -391,22 +393,36 @@ func SetLocalOption(option, value string, view *View) error {
|
||||
|
||||
if option == "fastdirty" {
|
||||
// If it is being turned off, we have to hash every open buffer
|
||||
var empty [16]byte
|
||||
var empty [md5.Size]byte
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, tab := range tabs {
|
||||
for _, v := range tab.Views {
|
||||
if !nativeValue.(bool) {
|
||||
if v.Buf.origHash == empty {
|
||||
data, err := ioutil.ReadFile(v.Buf.AbsPath)
|
||||
if err != nil {
|
||||
data = []byte{}
|
||||
}
|
||||
v.Buf.origHash = md5.Sum(data)
|
||||
wg.Add(1)
|
||||
|
||||
go func(b *Buffer) { // calculate md5 hash of the file
|
||||
defer wg.Done()
|
||||
|
||||
if file, e := os.Open(b.AbsPath); e == nil {
|
||||
defer file.Close()
|
||||
|
||||
h := md5.New()
|
||||
|
||||
if _, e = io.Copy(h, file); e == nil {
|
||||
h.Sum(b.origHash[:0])
|
||||
}
|
||||
}
|
||||
}(v.Buf)
|
||||
}
|
||||
} else {
|
||||
v.Buf.IsModified = v.Buf.Modified()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
buf.Settings[option] = nativeValue
|
||||
|
||||
@@ -11,7 +11,9 @@ import (
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Util.go is a collection of utility functions that are used throughout
|
||||
@@ -356,11 +358,43 @@ func ReplaceHome(path string) string {
|
||||
return strings.Replace(path, homeString, home, 1)
|
||||
}
|
||||
|
||||
// GetPath returns a filename without everything following a `:`
|
||||
// GetPathAndCursorPosition returns a filename without everything following a `:`
|
||||
// This is used for opening files like util.go:10:5 to specify a line and column
|
||||
func GetPath(path string) string {
|
||||
if strings.Contains(path, ":") {
|
||||
path = strings.Split(path, ":")[0]
|
||||
// Special cases like Windows Absolute path (C:\myfile.txt:10:5) are handled correctly.
|
||||
func GetPathAndCursorPosition(path string) (string, []string) {
|
||||
re := regexp.MustCompile(`([\s\S]+?)(?::(\d+))(?::(\d+))?`)
|
||||
match := re.FindStringSubmatch(path)
|
||||
// no lines/columns were specified in the path, return just the path with no cursor location
|
||||
if len(match) == 0 {
|
||||
return path, nil
|
||||
} else if match[len(match)-1] != "" {
|
||||
// if the last capture group match isn't empty then both line and column were provided
|
||||
return match[1], match[2:]
|
||||
}
|
||||
return path
|
||||
// if it was empty, then only a line was provided, so default to column 0
|
||||
return match[1], []string{match[2], "0"}
|
||||
}
|
||||
|
||||
func ParseCursorLocation(cursorPositions []string) (Loc, error) {
|
||||
startpos := Loc{0, 0}
|
||||
var err error
|
||||
|
||||
// if no positions are available exit early
|
||||
if cursorPositions == nil {
|
||||
return startpos, errors.New("No cursor positions were provided.")
|
||||
}
|
||||
|
||||
startpos.Y, err = strconv.Atoi(cursorPositions[0])
|
||||
if err != nil {
|
||||
messenger.Error("Error parsing cursor position: ", err)
|
||||
} else {
|
||||
if len(cursorPositions) > 1 {
|
||||
startpos.X, err = strconv.Atoi(cursorPositions[1])
|
||||
if err != nil {
|
||||
messenger.Error("Error parsing cursor position: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return startpos, err
|
||||
}
|
||||
|
||||
@@ -103,3 +103,229 @@ func TestWidthOfLargeRunes(t *testing.T) {
|
||||
t.Error("WidthOfLargeRunes 5 Failed. Got", w)
|
||||
}
|
||||
}
|
||||
|
||||
func assertEqual(t *testing.T, expected interface{}, result interface{}) {
|
||||
if expected != result {
|
||||
t.Fatalf("Expected: %d != Got: %d", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func assertTrue(t *testing.T, condition bool) {
|
||||
if !condition {
|
||||
t.Fatalf("Condition was not true. Got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPathRelativeWithDot(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("./myfile:10:5")
|
||||
|
||||
assertEqual(t, path, "./myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
assertEqual(t, "5", cursorPosition[1])
|
||||
}
|
||||
func TestGetPathRelativeWithDotWindows(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition(".\\myfile:10:5")
|
||||
|
||||
assertEqual(t, path, ".\\myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
assertEqual(t, cursorPosition[1], "5")
|
||||
}
|
||||
func TestGetPathRelativeNoDot(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("myfile:10:5")
|
||||
|
||||
assertEqual(t, path, "myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
|
||||
assertEqual(t, cursorPosition[1], "5")
|
||||
}
|
||||
func TestGetPathAbsoluteWindows(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("C:\\myfile:10:5")
|
||||
|
||||
assertEqual(t, path, "C:\\myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
|
||||
assertEqual(t, cursorPosition[1], "5")
|
||||
|
||||
path, cursorPosition = GetPathAndCursorPosition("C:/myfile:10:5")
|
||||
|
||||
assertEqual(t, path, "C:/myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
assertEqual(t, "5", cursorPosition[1])
|
||||
}
|
||||
|
||||
func TestGetPathAbsoluteUnix(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("/home/user/myfile:10:5")
|
||||
|
||||
assertEqual(t, path, "/home/user/myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
assertEqual(t, "5", cursorPosition[1])
|
||||
}
|
||||
|
||||
func TestGetPathRelativeWithDotWithoutLineAndColumn(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("./myfile")
|
||||
|
||||
assertEqual(t, path, "./myfile")
|
||||
// no cursor position in filename, nil should be returned
|
||||
assertTrue(t, cursorPosition == nil)
|
||||
}
|
||||
func TestGetPathRelativeWithDotWindowsWithoutLineAndColumn(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition(".\\myfile")
|
||||
|
||||
assertEqual(t, path, ".\\myfile")
|
||||
assertTrue(t, cursorPosition == nil)
|
||||
|
||||
}
|
||||
func TestGetPathRelativeNoDotWithoutLineAndColumn(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("myfile")
|
||||
|
||||
assertEqual(t, path, "myfile")
|
||||
assertTrue(t, cursorPosition == nil)
|
||||
|
||||
}
|
||||
func TestGetPathAbsoluteWindowsWithoutLineAndColumn(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("C:\\myfile")
|
||||
|
||||
assertEqual(t, path, "C:\\myfile")
|
||||
assertTrue(t, cursorPosition == nil)
|
||||
|
||||
path, cursorPosition = GetPathAndCursorPosition("C:/myfile")
|
||||
|
||||
assertEqual(t, path, "C:/myfile")
|
||||
assertTrue(t, cursorPosition == nil)
|
||||
|
||||
}
|
||||
func TestGetPathAbsoluteUnixWithoutLineAndColumn(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("/home/user/myfile")
|
||||
|
||||
assertEqual(t, path, "/home/user/myfile")
|
||||
assertTrue(t, cursorPosition == nil)
|
||||
|
||||
}
|
||||
func TestGetPathSingleLetterFileRelativePath(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("a:5:6")
|
||||
|
||||
assertEqual(t, path, "a")
|
||||
assertEqual(t, "5", cursorPosition[0])
|
||||
assertEqual(t, "6", cursorPosition[1])
|
||||
}
|
||||
func TestGetPathSingleLetterFileAbsolutePathWindows(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("C:\\a:5:6")
|
||||
|
||||
assertEqual(t, path, "C:\\a")
|
||||
assertEqual(t, "5", cursorPosition[0])
|
||||
assertEqual(t, "6", cursorPosition[1])
|
||||
|
||||
path, cursorPosition = GetPathAndCursorPosition("C:/a:5:6")
|
||||
|
||||
assertEqual(t, path, "C:/a")
|
||||
assertEqual(t, "5", cursorPosition[0])
|
||||
assertEqual(t, "6", cursorPosition[1])
|
||||
}
|
||||
func TestGetPathSingleLetterFileAbsolutePathUnix(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("/home/user/a:5:6")
|
||||
|
||||
assertEqual(t, path, "/home/user/a")
|
||||
assertEqual(t, "5", cursorPosition[0])
|
||||
assertEqual(t, "6", cursorPosition[1])
|
||||
}
|
||||
func TestGetPathSingleLetterFileAbsolutePathWindowsWithoutLineAndColumn(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("C:\\a")
|
||||
|
||||
assertEqual(t, path, "C:\\a")
|
||||
assertTrue(t, cursorPosition == nil)
|
||||
|
||||
path, cursorPosition = GetPathAndCursorPosition("C:/a")
|
||||
|
||||
assertEqual(t, path, "C:/a")
|
||||
assertTrue(t, cursorPosition == nil)
|
||||
|
||||
}
|
||||
func TestGetPathSingleLetterFileAbsolutePathUnixWithoutLineAndColumn(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("/home/user/a")
|
||||
|
||||
assertEqual(t, path, "/home/user/a")
|
||||
assertTrue(t, cursorPosition == nil)
|
||||
|
||||
}
|
||||
|
||||
func TestGetPathRelativeWithDotOnlyLine(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("./myfile:10")
|
||||
|
||||
assertEqual(t, path, "./myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
assertEqual(t, "0", cursorPosition[1])
|
||||
}
|
||||
func TestGetPathRelativeWithDotWindowsOnlyLine(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition(".\\myfile:10")
|
||||
|
||||
assertEqual(t, path, ".\\myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
assertEqual(t, "0", cursorPosition[1])
|
||||
}
|
||||
func TestGetPathRelativeNoDotOnlyLine(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("myfile:10")
|
||||
|
||||
assertEqual(t, path, "myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
assertEqual(t, "0", cursorPosition[1])
|
||||
}
|
||||
func TestGetPathAbsoluteWindowsOnlyLine(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("C:\\myfile:10")
|
||||
|
||||
assertEqual(t, path, "C:\\myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
assertEqual(t, "0", cursorPosition[1])
|
||||
|
||||
path, cursorPosition = GetPathAndCursorPosition("C:/myfile:10")
|
||||
|
||||
assertEqual(t, path, "C:/myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
assertEqual(t, "0", cursorPosition[1])
|
||||
}
|
||||
func TestGetPathAbsoluteUnixOnlyLine(t *testing.T) {
|
||||
path, cursorPosition := GetPathAndCursorPosition("/home/user/myfile:10")
|
||||
|
||||
assertEqual(t, path, "/home/user/myfile")
|
||||
assertEqual(t, "10", cursorPosition[0])
|
||||
assertEqual(t, "0", cursorPosition[1])
|
||||
}
|
||||
func TestParseCursorLocationOneArg(t *testing.T) {
|
||||
location, err := ParseCursorLocation([]string{"3"})
|
||||
|
||||
assertEqual(t, 3, location.Y)
|
||||
assertEqual(t, 0, location.X)
|
||||
assertEqual(t, nil, err)
|
||||
}
|
||||
func TestParseCursorLocationTwoArgs(t *testing.T) {
|
||||
location, err := ParseCursorLocation([]string{"3", "15"})
|
||||
|
||||
assertEqual(t, 3, location.Y)
|
||||
assertEqual(t, 15, location.X)
|
||||
assertEqual(t, nil, err)
|
||||
}
|
||||
func TestParseCursorLocationNoArgs(t *testing.T) {
|
||||
location, err := ParseCursorLocation(nil)
|
||||
// the expected result is the start position - 0, 0
|
||||
assertEqual(t, 0, location.Y)
|
||||
assertEqual(t, 0, location.X)
|
||||
// an error will be present here as the positions we're parsing are a nil
|
||||
assertTrue(t, err != nil)
|
||||
}
|
||||
func TestParseCursorLocationFirstArgNotValidNumber(t *testing.T) {
|
||||
// the messenger is necessary as ParseCursorLocation
|
||||
// puts a message in it on error
|
||||
messenger = new(Messenger)
|
||||
_, err := ParseCursorLocation([]string{"apples", "1"})
|
||||
// the expected result is the start position - 0, 0
|
||||
assertTrue(t, messenger.hasMessage)
|
||||
assertTrue(t, err != nil)
|
||||
}
|
||||
func TestParseCursorLocationSecondArgNotValidNumber(t *testing.T) {
|
||||
// the messenger is necessary as ParseCursorLocation
|
||||
// puts a message in it on error
|
||||
messenger = new(Messenger)
|
||||
_, err := ParseCursorLocation([]string{"1", "apples"})
|
||||
// the expected result is the start position - 0, 0
|
||||
assertTrue(t, messenger.hasMessage)
|
||||
assertTrue(t, err != nil)
|
||||
}
|
||||
|
||||
@@ -198,7 +198,10 @@ func (v *View) ToggleTabbar() {
|
||||
}
|
||||
|
||||
func (v *View) paste(clip string) {
|
||||
leadingWS := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
|
||||
leadingWS := ""
|
||||
if v.Cursor.X > 0 {
|
||||
leadingWS = GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
|
||||
}
|
||||
|
||||
if v.Cursor.HasSelection() {
|
||||
v.Cursor.DeleteSelection()
|
||||
|
||||
@@ -187,6 +187,8 @@ Start
|
||||
End
|
||||
PageUp
|
||||
PageDown
|
||||
SelectPageUp
|
||||
SelectPageDown
|
||||
HalfPageUp
|
||||
HalfPageDown
|
||||
StartOfLine
|
||||
@@ -352,6 +354,7 @@ CtrlRightSq
|
||||
CtrlCarat
|
||||
CtrlUnderscore
|
||||
Backspace
|
||||
OldBackspace
|
||||
Tab
|
||||
Esc
|
||||
Escape
|
||||
|
||||
99
runtime/syntax/ats.yaml
Normal file
99
runtime/syntax/ats.yaml
Normal file
@@ -0,0 +1,99 @@
|
||||
filetype: ATS
|
||||
|
||||
detect:
|
||||
filename: "\\.(d|h|s)ats$"
|
||||
|
||||
rules:
|
||||
- default: \b[[:alnum:]]+[^0-9A-Za-z]
|
||||
|
||||
# Operators
|
||||
- symbol.operator: "[.:;+`|~<>?='\\&]|/|%|-|,|!|\\*|@|\\#"
|
||||
- symbol.operator: \^
|
||||
|
||||
# Types, abstract types and some predefined sorts
|
||||
- type: \b(addr|age?z|bool|char|cls|schar|uchar|double|ldouble|eff|exn|float|int(ptr)?|lincloptr|uint)\b
|
||||
- type: \b(lint|ulint|llint|ullint|nat|ptr|real|ref|size_t|ssize_t|sint|usint|string|tkind|viewt|v?t0p|vt|void)\b
|
||||
- type: \b(prop|t[@0]ype|type|viewt[@0]ype|viewtype|vt[@0]ype|vtype|view)\b
|
||||
- type: \b(prop[+-]|t[@0]ype[+-]|type[+-]|viewt[@0]ype[+-]|viewtype[+-]|vt[@0]ype[+-]|vtype[+-]|view[+-])
|
||||
|
||||
# Statements
|
||||
- statement: \b(abstype|abst|absprop|absviewt|absvt(ype)?|absview|and|andalso|as|(re)?assume|begin|(pr)?case|s?case)\b
|
||||
- statement: \b(classdec|dataprop|data(v|view)?type|dataview|datasort|do|dynload|else|end|exception|extype|extva(r|l)|s?if)\b
|
||||
- statement: \b(ifcase|import|for|in|infix(l|r)?|let|local|macrodef|macdef|not|of|op|or|orelse|overload|(pre|post|non)fix)\b
|
||||
- statement: \b(propdef|rec|sortdef|stacst|stadef|staload|stavar|sta(tic)?|symelim|symintr|tkindef|then|try|viewdef|v?typedef)\b
|
||||
- statement: \b(viewtypedef|(pr)?va(l|r)|when|where|while|with|withtype|withprop|withv(iew)?type|withview)\b
|
||||
- statement: \b(abst[@0]ype|absviewt[@0]?ype|absvt[@0]ype|abstbox|abstflat|absvtbox|absvtflat|absimpl|absreimpl|abs)\b
|
||||
- statement: \b(case[+-]|(pr)?va(l|r)[+-]|for\*|while\*)
|
||||
|
||||
# Numbers
|
||||
- constant.number: \b[0-9.]+([eE][+-]?[0-9]+)?[fFlL]?\b
|
||||
- constant.number: \b0[xX][0-9A-Fa-f]*(\.[0-9A-Fa-f]*)?[pP][+-]?[0-9]+[fFlL]?\b
|
||||
- constant.number: \b([0-9]+|0[xX][0-9A-Fa-f]+)[lLuU]*\b
|
||||
|
||||
# Function-related keywords, special functions and namespaces. Not really identifiers
|
||||
- identifier: \b(fix|(pr)?fu?n|fnx|castfn|praxi|extern|lam|llam|(pr)?implement|(pr)?implmnt)\b
|
||||
- identifier: \b(fix@|fold@|free@|lam@|llam@|addr@|view@|ref@|fn\*)
|
||||
- identifier: \$\w*\b
|
||||
|
||||
# Other keywords, function effects...
|
||||
- special: (\$(arrpsz|arrptrsize|break|continue|d2ctype|delay|effmask_(ntm|exn|ref|wrt|all)))\b
|
||||
- special: (\$(effmask|extern|extype_struct|extype|extkind|extval|extfcall|extmcall|ldelay|literal))\b
|
||||
- special: (\$(li?st_vt|li?st_t|li?st|myfilename|myfunction|mylocation|raise|rec(ord)?_vt))\b
|
||||
- special: (\$(rec(ord)?_t|rec(ord)?|showtype|solver_assert|solver_verify|tempenver))\b
|
||||
- special: (\$(tup(le)?_vt|tup(le)?_t|tup(le)?|tyrep|vararg|vcopyenv_vt|vcopyenv_v))\b
|
||||
- special: \!(wrt|exnref|exnwrt|exn|refwrt|ref|all|ntm|laz)\b
|
||||
- special: \b(fun(0|1)|(lin)?cloptr(0|1)?|cloref(0|1)?|clo(0|1)?|lin|prf)\b
|
||||
- special: \b(f?print(ln)?!|prerr(ln)?!|tupz!)
|
||||
|
||||
# Some C macros and other ATS macros
|
||||
- preproc: ^[[:space:]]*#[[:space:]]*(define|pragma|include|(un|ifn?)def|endif|el(if|se)|if|warning|error|assert)\b
|
||||
- preproc: ^[[:space:]]*#[[:space:]]*(codegen2|codegen3|elifdef|elifndef|prerr|print|require|then|staload|dynload)\b
|
||||
|
||||
# Boolean values
|
||||
- constant.bool: \b(true|false|null)\b
|
||||
|
||||
|
||||
# The "%{ ... %}" block inserts foreign code into ATS at compile-time
|
||||
# Code inside of it is generally C or JavaScript
|
||||
- default:
|
||||
start: "%{[#^$]?"
|
||||
end: "%}"
|
||||
skip: "\\."
|
||||
limit-group: symbol.operator
|
||||
rules:
|
||||
- include: "c"
|
||||
- include: "javascript"
|
||||
|
||||
|
||||
# Strings and chars
|
||||
- constant.string: \"[^"]*\"
|
||||
- constant.string: \'[^']*\'
|
||||
|
||||
|
||||
# Comments
|
||||
# "////" comments everything until it reaches EOF
|
||||
- comment.block:
|
||||
start: ////
|
||||
end: $a
|
||||
rules:
|
||||
- todo: (TODO|XXX|FIXME)
|
||||
|
||||
- comment.line:
|
||||
start: //
|
||||
end: $
|
||||
rules:
|
||||
- todo: (TODO|XXX|FIXME)
|
||||
|
||||
# ML-like block comment
|
||||
- comment.block:
|
||||
start: \(\*
|
||||
end: \*\)
|
||||
rules:
|
||||
- todo: (TODO|XXX|FIXME)
|
||||
|
||||
# C-like block comment
|
||||
- comment.block:
|
||||
start: /\*
|
||||
end: \*\/
|
||||
rules:
|
||||
- todo: (TODO|XXX|FIXME)
|
||||
30
runtime/syntax/elixir.yaml
Normal file
30
runtime/syntax/elixir.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
filetype: elixir
|
||||
|
||||
detect:
|
||||
filename: "\\.ex$|\\.exs$"
|
||||
|
||||
rules:
|
||||
- statement: "\\b(abs|trunc|rem|div|round|max|min|and|or|not|throw|raise|reraise|hd|tl|in|length|elem|put_elem|destructure|to_(string|charlist)|is_(atom|binary|bitstring|boolean|float|function|integer|list|map|nil|number|pid|port|reference|tuple)|(bit|byte|map|tuple)_size|binary_part|def(delegate|exception|guard|guardp|impl|macro|macrop|module|overridable|p|protocol|struct)?|sigil_[crswCRSWDNT]|if|else|unless|cond|binding|node|self|spawn|spawn_link|spawn_monitor|send|exit|struct|get_and_update_in|get_in|put_in|pop_in|update_in|apply|inspect|make_ref|use|do|end)\\b"
|
||||
- statement: "\\b(alias|import|require|case|fn|receive|after|try|catch|rescue|super|quote|unquote|unquote_splicing|for|with)\\b"
|
||||
|
||||
- constant: "\\b\\[A-Z]+\\b"
|
||||
- constant.number: "\\b[0-9]+\\b"
|
||||
|
||||
- constant.string: "`[^`]*`|%x\\{[^}]*\\}"
|
||||
- constant.string: "\"([^\"]|(\\\\\"))*\"|%[QW]?\\{[^}]*\\}|%[QW]?\\([^)]*\\)|%[QW]?<[^>]*>|%[QW]?\\[[^]]*\\]|%[QW]?\\$[^$]*\\$|%[QW]?\\^[^^]*\\^|%[QW]?![^!]*!"
|
||||
- constant.string: "'([^']|(\\\\'))*'|%[qw]\\{[^}]*\\}|%[qw]\\([^)]*\\)|%[qw]<[^>]*>|%[qw]\\[[^]]*\\]|%[qw]\\$[^$]*\\$|%[qw]\\^[^^]*\\^|%[qw]![^!]*!"
|
||||
|
||||
- symbol.brackets: "\\{|\\}|\\[|\\]|\\(|\\)"
|
||||
|
||||
- comment: "#[^{].*$|#$"
|
||||
- comment.bright: "##[^{].*$|##$"
|
||||
|
||||
- type.keyword: "\\:[a-zA-Z][a-zA-Z0-9_]*"
|
||||
- type.keyword: "\\b(describe|test)"
|
||||
- statement: "\\b(expected|assert|assert_raise|assert_in_delta|assert_received|catch_error|catch_throw|flunk|refute|refute_in_delta|refute_received)\\b"
|
||||
- symbol.tag: "^\\s*\\@[a-zA-Z][a-zA-Z0-9_]*\\b"
|
||||
|
||||
- identifier.macro: "\\b(__CALLER__|__DIR__|__ENV__|__MODULE__|__aliases__|__block__|defmacro)\\b"
|
||||
|
||||
- todo: "(XXX|TODO|FIXME|\\?\\?\\?)"
|
||||
- preproc.shebang: "\\W*#!.+?( |$)"
|
||||
48
runtime/syntax/fsharp.yaml
Normal file
48
runtime/syntax/fsharp.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
filetype: fsharp
|
||||
|
||||
detect:
|
||||
filename: "\\.fs?$"
|
||||
|
||||
rules:
|
||||
- identifier: "\\b[A-Z][0-9a-z_]{2,}\\b"
|
||||
#declarations
|
||||
- statement: "\\b(let|val|method|in|and|rec|private|virtual|constraint)\\b"
|
||||
#structure items
|
||||
- type: "\\b(type|open|class|module|exception|external)\\b"
|
||||
#patterns
|
||||
- statement: "\\b(fun|function|functor|match|try|with)\\b"
|
||||
#patterns-modifiers
|
||||
- statement: "\\b(as|when|of)\\b"
|
||||
#conditions
|
||||
- statement: "\\b(if|then|else)\\b"
|
||||
#blocs
|
||||
- type: "\\b(begin|end|object|struct|sig|for|while|do|done|to|downto)\\b"
|
||||
#constantes
|
||||
- constant.bool: "\\b(true|false)\\b"
|
||||
#modules/classes
|
||||
- special: "\\b(include|inherit|initializer)\\b"
|
||||
#expr modifiers
|
||||
- special: "\\b(new|ref|mutable|lazy|assert|raise)\\b"
|
||||
#keywords which don't exist in ocaml
|
||||
- type: "\\b(base|delegate|downcast|extern|finally|fixed|global|inline|interface|internal|let!|member|namespace|null|override|private|public)\\b"
|
||||
- type: "\\b(return|return!|select|static|upcast|use|use!|void|yield|yield!)\\b"
|
||||
- constant.string:
|
||||
start: "'"
|
||||
end: "'"
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "%."
|
||||
- constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]"
|
||||
- constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})"
|
||||
- constant.string:
|
||||
start: "\""
|
||||
end: "\""
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "%."
|
||||
- constant.specialChar: "\\\\[abfnrtv'\\\"\\\\]"
|
||||
- constant.specialChar: "\\\\([0-7]{3}|x[A-Fa-f0-9]{2}|u[A-Fa-f0-9]{4}|U[A-Fa-f0-9]{8})"
|
||||
- comment:
|
||||
start: "\\(\\*"
|
||||
end: "\\*\\)"
|
||||
rules: []
|
||||
@@ -13,7 +13,7 @@ rules:
|
||||
# definitions
|
||||
- identifier: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[(]"
|
||||
# keywords
|
||||
- statement: "\\b(begin|break|catch|continue|function|elseif|else|end|finally|for|global|local|const|if|include|import|using|require|macro|println|return|try|type|while|module)\\b"
|
||||
- statement: "\\b(begin|break|catch|continue|function|elseif|struct|else|end|finally|for|global|local|const|if|include|import|using|require|macro|println|return|try|type|while|module)\\b"
|
||||
# decorators
|
||||
- identifier.macro: "@[A-Za-z0-9_]+"
|
||||
# operators
|
||||
|
||||
@@ -18,10 +18,10 @@ rules:
|
||||
- comment: "<!--.+?-->"
|
||||
- default: "<\\?(php|=)\" end=\"\\?>"
|
||||
- identifier.class: "([a-zA-Z0-9_-]+)\\("
|
||||
- preproc: "\\b(require|include)(_once)?)\\b"
|
||||
- preproc: "\\b(require|include)(_once)?\\b"
|
||||
- type: "\\b(var|class|extends|function|echo|case|default|exit|switch|extends|as|define|do|declare|in|trait|interface|[E|e]xception|array|int|string|bool|iterable|void)\\b"
|
||||
- identifier.class: "[a-zA-Z\\\\]+::"
|
||||
- identifier: "([A-Z][a-zA-Z0-9_]+)\\s"
|
||||
- identifier: "\\b([A-Z][a-zA-Z0-9_]+)\\b"
|
||||
- identifier: "([A-Z0-9_]+)[;|\\s|\\)|,]"
|
||||
- type.keyword: "(global|public|private|protected|static|const)"
|
||||
- statement: "(implements|abstract|instanceof|if|else(if)?|endif|namespace|use|as|new|throw|catch|try|while|print|(end)?(foreach)?)\\b"
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
# This script creates the nightly release on Github for micro
|
||||
# You must have the correct Github access token to run this script
|
||||
|
||||
commitID=$(git rev-parse HEAD)
|
||||
info=$(github-release info -u zyedidia -r micro -t nightly)
|
||||
|
||||
if [[ $info = *$commitID* ]]; then
|
||||
echo "No new commits since last nightly"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Deleting old release"
|
||||
github-release delete \
|
||||
--user zyedidia \
|
||||
--repo micro \
|
||||
--tag nightly
|
||||
|
||||
commitID=$(git rev-parse HEAD)
|
||||
|
||||
echo "Moving tag"
|
||||
git tag --force nightly $commitID
|
||||
git push --force --tags
|
||||
|
||||
Reference in New Issue
Block a user