Compare commits

..

16 Commits

Author SHA1 Message Date
Zachary Yedidia
1856891622 Update nightly release script to not duplicate nightlies 2018-07-20 00:24:02 +00:00
djmnzp
8a250f7d95 Update ats syntax (#1141)
* Multiple changes
 - Fixed overlapping between the macros and some statements.
 - Added "t" and "abs" as types.
 - Removed "fun0", "fun1", "clo0", "clo1", ..., "prf" from types and added them to the special block as effects.
 - Added "lin", "lincloptr0" and "lincloptr1" as effects.
 - Added "do" and "static" as statements.
 - Added "tupz!" and "prerr!" to the special block.
 - Fixed some typos.

* Updated regex for exhaustive types

* Final touches

* Removed "t" from types

* Minor fix

* Improved support for floats and integers
Make it comply with https://github.com/Hibou57/PostiATS-Utilities/blob/master/doc/lexemes-guide.md

* Chars are now interpreted as strings
Less troubling when working with '"' inside chars or multiline strings

* Reverted strings and chars from multiline to one line
For some reason, having strings on the same line as other symbols breaks the highlighting on the latter

* Add "ldouble" type
2018-07-16 15:37:57 -04:00
Zachary Yedidia
7a013f666e Update runtime and auto-gofmt runtime in make 2018-07-02 12:22:32 -04:00
Zachary Yedidia
41a24e61d6 Merge pull request #1135 from whilei/gofmt-2018-Jun-17-00-39
gofmt
2018-07-02 12:22:05 -04:00
djmnzp
d953339a56 Added syntax highlighting for ATS (#1137)
* Added syntax highlighting for ATS

* Fixed "////" comment not working as intended
Added a hack to make it impossible to match the end of the comment

* Fixed typo, added '#' and '@' as symbols
2018-07-02 12:19:38 -04:00
ia
76e1d7a3a7 all: gofmt
Run standard gofmt command on project root.

- go version go1.10.3 darwin/amd64

Signed-off-by: ia <isaac.ardis@gmail.com>
2018-06-17 00:41:57 +02:00
Zachary Yedidia
91b65001c9 Fix php syntax file
Fixes #1109
2018-06-04 15:13:58 -04:00
Dimitar Borislavov Tasev
aa74b1233c Fix -startpos flag being ignored (#1129)
* Refactored cursor location login into a function. Fixed buffer overflow when line position is 1 more than file lines

* Fixed crash when -startpos has an invalid argument

* Adapted tests to new interface

* Fixed bug where -startpos with lines 0 and 1 would both be on the first line

* Changed Fatalf format back to digits

* Fixed issues with buffer cursor location. Added tests for new function

* ParseCursorLocation will now return an error when path doesnt contain line/col

* Fixed off-by-one line error

* Fixed tests to account for subtracting 1 from the line index
2018-06-04 12:27:27 -04:00
Zachary Yedidia
61baa73d70 Merge pull request #1125 from nabeelomer/master
F# Configuration
2018-06-03 17:13:22 -04:00
Dimitar Borislavov Tasev
efe343b37c Allows opening files using full path on Windows (#1126)
* Now can open Windows full-path from command line arg

Example that now works: micro.exe D:\myfile.txt

* Now correctly retrieves the path from the input path string. Except for single-letter filenames

* Fixed line/cols, need to make the code prettier

* Fixed path matching with regex by @Pariador

* Fixed not stripping the line/col args from file path

* Added tests for ParseCursorLocation
2018-06-03 17:13:03 -04:00
Nabeel Omer
cc8e9a7e06 F# Configuration 2018-05-29 20:02:58 +05:30
Sean Charles
d7f7d845b9 Elixir configuration (#1118)
* Elixir configuration

* added exunit support

* end added
2018-05-26 10:08:35 -04:00
Zachary Yedidia
8f0418c9a8 Merge pull request #1119 from mbesancon/patch-3
Update julia.yaml
2018-05-26 10:08:19 -04:00
Maxim
71af765b4e Code optimisation (#1117)
* Making sure output files are always closed, plus hash calculation optimisation.

* Parallel hash calculation.

* Minor changes.

* Removed unnecessary memory allocations while trimming trailing whitespace.

* Buffered write.
2018-05-26 10:07:53 -04:00
mbesancon
c0f279ffe8 Update julia.yaml
added struct to keywords
2018-05-25 12:04:12 -04:00
JT Olio
ae9bb763fb a few miscellaneous fixes and improvements (#1105)
* add binding for more primitive backspace

* support selecting page up and page down

* fix matchbraceleft for braces that start on x=0

* fix multiline copy-paste indenting

let's say you have two lines like

  <space><space>line1
  <space><space>line2

so you start from cursor x=0 and select both lines, then paste.
we don't want any leading whitespace in this case, because the
cursor is already at x=0 and the selection already includes
whitespace.
2018-05-12 21:31:57 -04:00
20 changed files with 1214 additions and 473 deletions

View File

@@ -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

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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
View 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)
//}

View File

@@ -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)
}
}

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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()

View File

@@ -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
View 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)

View 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*#!.+?( |$)"

View 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: []

View File

@@ -13,7 +13,7 @@ rules:
# definitions
- identifier: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[(]"
# keywords
- statement: "\\b(begin|break|catch|continue|function|elseif|else|end|finally|for|global|local|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

View File

@@ -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"

View File

@@ -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