mirror of
https://github.com/zyedidia/micro.git
synced 2026-04-01 23:49:49 +09:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a02ae3ceed | ||
|
|
54c02f4781 | ||
|
|
a5e721b107 | ||
|
|
12a4dd58f3 | ||
|
|
5a7ddb8330 | ||
|
|
cb75531818 | ||
|
|
6229a0579f | ||
|
|
fb980bb695 | ||
|
|
19dc9d7bbc | ||
|
|
1e55b6f6b3 | ||
|
|
0a35bfe2f5 | ||
|
|
2f587c6d48 | ||
|
|
5b426aee86 | ||
|
|
f700769b27 | ||
|
|
04b672eebe | ||
|
|
f7238e8e53 | ||
|
|
33cb39d318 |
@@ -50,7 +50,7 @@ You can also check out the website for Micro at https://micro-editor.github.io.
|
||||
* Macros
|
||||
* Common editor things such as undo/redo, line numbers, Unicode support, softwrap...
|
||||
|
||||
Although not yet implemented, I hope to add more features such as autocompletion ([#174](https://github.com/zyedidia/micro/issues/174)), and multiple cursors ([#5](https://github.com/zyedidia/micro/issues/5)) in the future.
|
||||
Although not yet implemented, I hope to add more features such as autocompletion ([#174](https://github.com/zyedidia/micro/issues/174)) or a tree view ([#249](https://github.com/zyedidia/micro/issues/249)) in the future.
|
||||
|
||||
# Installation
|
||||
|
||||
|
||||
@@ -62,71 +62,54 @@ func (v *View) MousePress(usePlugin bool, e *tcell.EventMouse) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
rawX, rawY := e.Position()
|
||||
|
||||
x := rawX - v.lineNumOffset - v.leftCol + v.x
|
||||
y := rawY + v.Topline - v.y
|
||||
x, y := e.Position()
|
||||
x -= v.lineNumOffset - v.leftCol + v.x
|
||||
y += v.Topline - v.y
|
||||
|
||||
// This is usually bound to left click
|
||||
if !(rawX == v.x+v.Width || rawY == v.y+v.Height || v.resizeX || v.resizeY) {
|
||||
v.MoveToMouseClick(x, y)
|
||||
}
|
||||
v.MoveToMouseClick(x, y)
|
||||
if v.mouseReleased {
|
||||
if rawX == v.x+v.Width {
|
||||
v.resizeX = true
|
||||
} else if rawY == v.y+v.Height {
|
||||
v.resizeY = true
|
||||
} else {
|
||||
if len(v.Buf.cursors) > 1 {
|
||||
for i := 1; i < len(v.Buf.cursors); i++ {
|
||||
v.Buf.cursors[i] = nil
|
||||
}
|
||||
v.Buf.cursors = v.Buf.cursors[:1]
|
||||
v.Buf.UpdateCursors()
|
||||
v.Cursor.ResetSelection()
|
||||
v.Relocate()
|
||||
if len(v.Buf.cursors) > 1 {
|
||||
for i := 1; i < len(v.Buf.cursors); i++ {
|
||||
v.Buf.cursors[i] = nil
|
||||
}
|
||||
if time.Since(v.lastClickTime)/time.Millisecond < doubleClickThreshold {
|
||||
if v.doubleClick {
|
||||
// Triple click
|
||||
v.lastClickTime = time.Now()
|
||||
|
||||
v.tripleClick = true
|
||||
v.doubleClick = false
|
||||
|
||||
v.Cursor.SelectLine()
|
||||
v.Cursor.CopySelection("primary")
|
||||
} else {
|
||||
// Double click
|
||||
v.lastClickTime = time.Now()
|
||||
|
||||
v.doubleClick = true
|
||||
v.tripleClick = false
|
||||
|
||||
v.Cursor.SelectWord()
|
||||
v.Cursor.CopySelection("primary")
|
||||
}
|
||||
} else {
|
||||
v.doubleClick = false
|
||||
v.tripleClick = false
|
||||
v.Buf.cursors = v.Buf.cursors[:1]
|
||||
v.Buf.UpdateCursors()
|
||||
v.Cursor.ResetSelection()
|
||||
v.Relocate()
|
||||
}
|
||||
if time.Since(v.lastClickTime)/time.Millisecond < doubleClickThreshold && (x == v.lastLoc.X && y == v.lastLoc.Y) {
|
||||
if v.doubleClick {
|
||||
// Triple click
|
||||
v.lastClickTime = time.Now()
|
||||
|
||||
v.Cursor.OrigSelection[0] = v.Cursor.Loc
|
||||
v.Cursor.CurSelection[0] = v.Cursor.Loc
|
||||
v.Cursor.CurSelection[1] = v.Cursor.Loc
|
||||
v.tripleClick = true
|
||||
v.doubleClick = false
|
||||
|
||||
v.Cursor.SelectLine()
|
||||
v.Cursor.CopySelection("primary")
|
||||
} else {
|
||||
// Double click
|
||||
v.lastClickTime = time.Now()
|
||||
|
||||
v.doubleClick = true
|
||||
v.tripleClick = false
|
||||
|
||||
v.Cursor.SelectWord()
|
||||
v.Cursor.CopySelection("primary")
|
||||
}
|
||||
} else {
|
||||
v.doubleClick = false
|
||||
v.tripleClick = false
|
||||
v.lastClickTime = time.Now()
|
||||
|
||||
v.Cursor.OrigSelection[0] = v.Cursor.Loc
|
||||
v.Cursor.CurSelection[0] = v.Cursor.Loc
|
||||
v.Cursor.CurSelection[1] = v.Cursor.Loc
|
||||
}
|
||||
v.mouseReleased = false
|
||||
} else if !v.mouseReleased {
|
||||
if v.resizeX {
|
||||
v.Width = rawX - v.x
|
||||
v.LockWidth = true
|
||||
v.splitNode.parent.ResizeSplits()
|
||||
} else if v.resizeY {
|
||||
v.Height = rawY - v.y
|
||||
v.LockHeight = true
|
||||
v.splitNode.parent.ResizeSplits()
|
||||
} else if v.tripleClick {
|
||||
if v.tripleClick {
|
||||
v.Cursor.AddLineToSelection()
|
||||
} else if v.doubleClick {
|
||||
v.Cursor.AddWordToSelection()
|
||||
@@ -136,6 +119,8 @@ func (v *View) MousePress(usePlugin bool, e *tcell.EventMouse) bool {
|
||||
}
|
||||
}
|
||||
|
||||
v.lastLoc = Loc{x, y}
|
||||
|
||||
if usePlugin {
|
||||
PostActionCall("MousePress", v, e)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
var pluginCompletions []func(string) []string
|
||||
@@ -22,13 +20,9 @@ func FileComplete(input string) (string, []string) {
|
||||
var files []os.FileInfo
|
||||
var err error
|
||||
if len(dirs) > 1 {
|
||||
home, _ := homedir.Dir()
|
||||
|
||||
directories := strings.Join(dirs[:len(dirs)-1], sep) + sep
|
||||
|
||||
if strings.HasPrefix(directories, "~") {
|
||||
directories = strings.Replace(directories, "~", home, 1)
|
||||
}
|
||||
directories = ReplaceHome(directories)
|
||||
files, err = ioutil.ReadDir(directories)
|
||||
} else {
|
||||
files, err = ioutil.ReadDir(".")
|
||||
@@ -148,6 +142,55 @@ func OptionComplete(input string) (string, []string) {
|
||||
return chosen, suggestions
|
||||
}
|
||||
|
||||
func OptionValueComplete(inputOpt, input string) (string, []string) {
|
||||
inputOpt = strings.TrimSpace(inputOpt)
|
||||
var suggestions []string
|
||||
localSettings := DefaultLocalSettings()
|
||||
var optionVal interface{}
|
||||
for k, option := range globalSettings {
|
||||
if k == inputOpt {
|
||||
optionVal = option
|
||||
}
|
||||
}
|
||||
for k, option := range localSettings {
|
||||
if k == inputOpt {
|
||||
optionVal = option
|
||||
}
|
||||
}
|
||||
|
||||
switch optionVal.(type) {
|
||||
case bool:
|
||||
if strings.HasPrefix("on", input) {
|
||||
suggestions = append(suggestions, "on")
|
||||
} else if strings.HasPrefix("true", input) {
|
||||
suggestions = append(suggestions, "true")
|
||||
}
|
||||
if strings.HasPrefix("off", input) {
|
||||
suggestions = append(suggestions, "off")
|
||||
} else if strings.HasPrefix("false", input) {
|
||||
suggestions = append(suggestions, "false")
|
||||
}
|
||||
case string:
|
||||
switch inputOpt {
|
||||
case "colorscheme":
|
||||
_, suggestions = ColorschemeComplete(input)
|
||||
case "fileformat":
|
||||
if strings.HasPrefix("unix", input) {
|
||||
suggestions = append(suggestions, "unix")
|
||||
}
|
||||
if strings.HasPrefix("dos", input) {
|
||||
suggestions = append(suggestions, "dos")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var chosen string
|
||||
if len(suggestions) == 1 {
|
||||
chosen = suggestions[0]
|
||||
}
|
||||
return chosen, suggestions
|
||||
}
|
||||
|
||||
// MakeCompletion registers a function from a plugin for autocomplete commands
|
||||
func MakeCompletion(function string) Completion {
|
||||
pluginCompletions = append(pluginCompletions, LuaFunctionComplete(function))
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/gob"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -15,7 +16,6 @@ import (
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/zyedidia/micro/cmd/micro/highlight"
|
||||
)
|
||||
|
||||
@@ -57,6 +57,9 @@ type Buffer struct {
|
||||
syntaxDef *highlight.Def
|
||||
highlighter *highlight.Highlighter
|
||||
|
||||
// Hash of the original buffer -- empty if fastdirty is on
|
||||
origHash [16]byte
|
||||
|
||||
// Buffer local settings
|
||||
Settings map[string]interface{}
|
||||
}
|
||||
@@ -174,7 +177,7 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
|
||||
}
|
||||
|
||||
if b.Settings["saveundo"].(bool) {
|
||||
// We should only use last time's eventhandler if the file wasn't by someone else in the meantime
|
||||
// We should only use last time's eventhandler if the file wasn't modified by someone else in the meantime
|
||||
if b.ModTime == buffer.ModTime {
|
||||
b.EventHandler = buffer.EventHandler
|
||||
b.EventHandler.buf = b
|
||||
@@ -184,8 +187,13 @@ func NewBuffer(reader io.Reader, size int64, path string) *Buffer {
|
||||
file.Close()
|
||||
}
|
||||
|
||||
if b.Settings["mouse"].(bool) {
|
||||
screen.EnableMouse()
|
||||
if !b.Settings["fastdirty"].(bool) {
|
||||
if size > 50000 {
|
||||
// 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()))
|
||||
}
|
||||
}
|
||||
|
||||
b.cursors = []*Cursor{&b.Cursor}
|
||||
@@ -381,7 +389,6 @@ func (b *Buffer) Serialize() error {
|
||||
// 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()
|
||||
dir, _ := homedir.Dir()
|
||||
if b.Settings["rmtrailingws"].(bool) {
|
||||
r, _ := regexp.Compile(`[ \t]+$`)
|
||||
for lineNum, line := range b.Lines(0, b.NumLines) {
|
||||
@@ -402,9 +409,9 @@ func (b *Buffer) SaveAs(filename string) error {
|
||||
}
|
||||
str := b.SaveString(b.Settings["fileformat"] == "dos")
|
||||
data := []byte(str)
|
||||
err := ioutil.WriteFile(filename, data, 0644)
|
||||
err := ioutil.WriteFile(ReplaceHome(filename), data, 0644)
|
||||
if err == nil {
|
||||
b.Path = strings.Replace(filename, "~", dir, 1)
|
||||
b.Path = filename
|
||||
b.IsModified = false
|
||||
b.ModTime, _ = GetModTime(filename)
|
||||
return b.Serialize()
|
||||
@@ -451,6 +458,13 @@ func (b *Buffer) SaveAsWithSudo(filename string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Buffer) Modified() bool {
|
||||
if b.Settings["fastdirty"].(bool) {
|
||||
return b.IsModified
|
||||
}
|
||||
return b.origHash != md5.Sum([]byte(b.String()))
|
||||
}
|
||||
|
||||
func (b *Buffer) insert(pos Loc, value []byte) {
|
||||
b.IsModified = true
|
||||
b.LineArray.insert(pos, value)
|
||||
|
||||
@@ -112,9 +112,6 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
|
||||
// We'll either draw the length of the line, or the width of the screen
|
||||
// whichever is smaller
|
||||
lineLength := min(StringWidth(lineStr, tabsize), width)
|
||||
if lineLength < 0 {
|
||||
return
|
||||
}
|
||||
c.lines = append(c.lines, make([]*Char, lineLength))
|
||||
|
||||
wrap := false
|
||||
@@ -136,50 +133,38 @@ func (c *CellView) Draw(buf *Buffer, top, height, left, width int) {
|
||||
char := line[colN]
|
||||
|
||||
if viewCol >= 0 {
|
||||
if viewCol < len(c.lines[viewLine]) {
|
||||
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, char, curStyle, 1}
|
||||
}
|
||||
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, char, curStyle, 1}
|
||||
}
|
||||
if char == '\t' {
|
||||
charWidth := tabsize - (viewCol+left)%tabsize
|
||||
if viewCol >= 0 {
|
||||
if viewCol < len(c.lines[viewLine]) {
|
||||
c.lines[viewLine][viewCol].drawChar = indentchar
|
||||
c.lines[viewLine][viewCol].width = charWidth
|
||||
}
|
||||
c.lines[viewLine][viewCol].drawChar = indentchar
|
||||
c.lines[viewLine][viewCol].width = charWidth
|
||||
|
||||
indentStyle := curStyle
|
||||
if group, ok := colorscheme["indent-char"]; ok {
|
||||
indentStyle = group
|
||||
}
|
||||
|
||||
if viewCol < len(c.lines[viewLine]) {
|
||||
c.lines[viewLine][viewCol].style = indentStyle
|
||||
}
|
||||
c.lines[viewLine][viewCol].style = indentStyle
|
||||
}
|
||||
|
||||
for i := 1; i < charWidth; i++ {
|
||||
viewCol++
|
||||
if viewCol >= 0 && viewCol < lineLength {
|
||||
if viewCol < len(c.lines[viewLine]) {
|
||||
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
|
||||
}
|
||||
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
|
||||
}
|
||||
}
|
||||
viewCol++
|
||||
} else if runewidth.RuneWidth(char) > 1 {
|
||||
charWidth := runewidth.RuneWidth(char)
|
||||
if viewCol >= 0 {
|
||||
if viewCol < len(c.lines[viewLine]) {
|
||||
c.lines[viewLine][viewCol].width = charWidth
|
||||
}
|
||||
c.lines[viewLine][viewCol].width = charWidth
|
||||
}
|
||||
for i := 1; i < charWidth; i++ {
|
||||
viewCol++
|
||||
if viewCol >= 0 && viewCol < lineLength {
|
||||
if viewCol < len(c.lines[viewLine]) {
|
||||
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
|
||||
}
|
||||
c.lines[viewLine][viewCol] = &Char{Loc{viewCol, viewLine}, Loc{colN, lineN}, char, ' ', curStyle, 1}
|
||||
}
|
||||
}
|
||||
viewCol++
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"strings"
|
||||
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// A Command contains a action (a function to call) as well as information about how to autocomplete the command
|
||||
@@ -90,7 +89,7 @@ func MakeCommand(name, function string, completions ...Completion) {
|
||||
// DefaultCommands returns a map containing micro's default commands
|
||||
func DefaultCommands() map[string]StrCommand {
|
||||
return map[string]StrCommand{
|
||||
"set": {"Set", []Completion{OptionCompletion, NoCompletion}},
|
||||
"set": {"Set", []Completion{OptionCompletion, OptionValueCompletion}},
|
||||
"setlocal": {"SetLocal", []Completion{OptionCompletion, NoCompletion}},
|
||||
"show": {"Show", []Completion{OptionCompletion, NoCompletion}},
|
||||
"bind": {"Bind", []Completion{NoCompletion}},
|
||||
@@ -230,8 +229,7 @@ func TabSwitch(args []string) {
|
||||
// Cd changes the current working directory
|
||||
func Cd(args []string) {
|
||||
if len(args) > 0 {
|
||||
home, _ := homedir.Dir()
|
||||
path := strings.Replace(args[0], "~", home, 1)
|
||||
path := ReplaceHome(args[0])
|
||||
os.Chdir(path)
|
||||
for _, tab := range tabs {
|
||||
for _, view := range tab.views {
|
||||
@@ -325,8 +323,7 @@ func VSplit(args []string) {
|
||||
CurView().VSplit(NewBufferFromString("", ""))
|
||||
} else {
|
||||
filename := args[0]
|
||||
home, _ := homedir.Dir()
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
filename = ReplaceHome(filename)
|
||||
file, err := os.Open(filename)
|
||||
fileInfo, _ := os.Stat(filename)
|
||||
|
||||
@@ -355,8 +352,7 @@ func HSplit(args []string) {
|
||||
CurView().HSplit(NewBufferFromString("", ""))
|
||||
} else {
|
||||
filename := args[0]
|
||||
home, _ := homedir.Dir()
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
filename = ReplaceHome(filename)
|
||||
file, err := os.Open(filename)
|
||||
fileInfo, _ := os.Stat(filename)
|
||||
|
||||
@@ -396,8 +392,7 @@ func NewTab(args []string) {
|
||||
CurView().AddTab(true)
|
||||
} else {
|
||||
filename := args[0]
|
||||
home, _ := homedir.Dir()
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
filename = ReplaceHome(filename)
|
||||
file, err := os.Open(filename)
|
||||
fileInfo, _ := os.Stat(filename)
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ func (c *Cursor) UpN(amount int) {
|
||||
runes := []rune(c.buf.Line(c.Y))
|
||||
c.X = c.GetCharPosInLine(proposedY, c.LastVisualX)
|
||||
|
||||
if c.X > len(runes) {
|
||||
if c.X > len(runes) || (amount < 0 && proposedY == c.Y) {
|
||||
c.X = len(runes)
|
||||
}
|
||||
|
||||
|
||||
@@ -231,6 +231,7 @@ const (
|
||||
OptionCompletion
|
||||
PluginCmdCompletion
|
||||
PluginNameCompletion
|
||||
OptionValueCompletion
|
||||
)
|
||||
|
||||
// Prompt sends the user a message and waits for a response to be typed in
|
||||
@@ -296,6 +297,10 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
|
||||
chosen, suggestions = HelpComplete(currentArg)
|
||||
} else if completionType == OptionCompletion {
|
||||
chosen, suggestions = OptionComplete(currentArg)
|
||||
} else if completionType == OptionValueCompletion {
|
||||
if currentArgNum-1 > 0 {
|
||||
chosen, suggestions = OptionValueComplete(args[currentArgNum-1], currentArg)
|
||||
}
|
||||
} else if completionType == PluginCmdCompletion {
|
||||
chosen, suggestions = PluginCmdComplete(currentArg)
|
||||
} else if completionType == PluginNameCompletion {
|
||||
|
||||
@@ -202,6 +202,10 @@ func InitScreen() {
|
||||
os.Setenv("TERM", oldTerm)
|
||||
}
|
||||
|
||||
if GetGlobalOption("mouse").(bool) {
|
||||
screen.EnableMouse()
|
||||
}
|
||||
|
||||
screen.SetStyle(defStyle)
|
||||
}
|
||||
|
||||
@@ -250,23 +254,31 @@ func LoadAll() {
|
||||
var flagVersion = flag.Bool("version", false, "Show the version number and information")
|
||||
var flagStartPos = flag.String("startpos", "", "LINE,COL to start the cursor at when opening a buffer.")
|
||||
var flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
|
||||
var optionFlagSet = flag.NewFlagSet("option", flag.ExitOnError)
|
||||
var flagOptions = flag.Bool("options", false, "Show all option help")
|
||||
|
||||
func main() {
|
||||
flag.Usage = func() {
|
||||
fmt.Println("Usage: micro [OPTIONS] [FILE]...")
|
||||
flag.CommandLine.SetOutput(os.Stdout)
|
||||
flag.PrintDefaults()
|
||||
optionFlagSet.SetOutput(os.Stdout)
|
||||
fmt.Print("\n------------------------------------------------------------------\n")
|
||||
fmt.Print("Micro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the bindings.json\nfile (see 'help options').\n\n")
|
||||
optionFlagSet.PrintDefaults()
|
||||
fmt.Println("-config-dir dir")
|
||||
fmt.Println(" \tSpecify a custom location for the configuration directory")
|
||||
fmt.Println("-startpos LINE,COL")
|
||||
fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
|
||||
fmt.Println("-options")
|
||||
fmt.Println(" \tShow all option help")
|
||||
fmt.Println("-version")
|
||||
fmt.Println(" \tShow the version number and information")
|
||||
|
||||
fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the bindings.json\nfile (see 'help options').\n\n")
|
||||
fmt.Println("-option value")
|
||||
fmt.Println(" \tSet `option` to `value` for this session")
|
||||
fmt.Println(" \tFor example: `micro -syntax off file.c`")
|
||||
fmt.Println("\nUse `micro -options` to see the full list of configuration options")
|
||||
}
|
||||
|
||||
optionFlags := make(map[string]*string)
|
||||
|
||||
for k, v := range DefaultGlobalSettings() {
|
||||
optionFlags[k] = optionFlagSet.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'", k, v))
|
||||
optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'", k, v))
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
@@ -279,6 +291,15 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if *flagOptions {
|
||||
// If -options was passed
|
||||
for k, v := range DefaultGlobalSettings() {
|
||||
fmt.Printf("-%s value\n", k)
|
||||
fmt.Printf(" \tThe %s option. Default value: '%v'\n", k, v)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Start the Lua VM for running plugins
|
||||
L = lua.NewState()
|
||||
defer L.Close()
|
||||
@@ -476,7 +497,7 @@ func main() {
|
||||
// We loop through each view in the current tab and make sure the current view
|
||||
// is the one being clicked in
|
||||
for _, v := range tabs[curTab].views {
|
||||
if x > v.x && x <= v.x+v.Width && y > v.y && y <= v.y+v.Height {
|
||||
if x >= v.x && x < v.x+v.Width && y >= v.y && y < v.y+v.Height {
|
||||
tabs[curTab].CurView = v.Num
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
@@ -198,10 +199,13 @@ func DefaultGlobalSettings() map[string]interface{} {
|
||||
"colorscheme": "default",
|
||||
"cursorline": true,
|
||||
"eofnewline": false,
|
||||
"rmtrailingws": false,
|
||||
"fastdirty": true,
|
||||
"fileformat": "unix",
|
||||
"ignorecase": false,
|
||||
"indentchar": " ",
|
||||
"infobar": true,
|
||||
"mouse": true,
|
||||
"rmtrailingws": false,
|
||||
"ruler": true,
|
||||
"savecursor": false,
|
||||
"saveundo": false,
|
||||
@@ -221,8 +225,6 @@ func DefaultGlobalSettings() map[string]interface{} {
|
||||
},
|
||||
"pluginrepos": []string{},
|
||||
"useprimary": true,
|
||||
"fileformat": "unix",
|
||||
"mouse": true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,10 +238,12 @@ func DefaultLocalSettings() map[string]interface{} {
|
||||
"colorcolumn": float64(0),
|
||||
"cursorline": true,
|
||||
"eofnewline": false,
|
||||
"rmtrailingws": false,
|
||||
"fastdirty": true,
|
||||
"fileformat": "unix",
|
||||
"filetype": "Unknown",
|
||||
"ignorecase": false,
|
||||
"indentchar": " ",
|
||||
"rmtrailingws": false,
|
||||
"ruler": true,
|
||||
"savecursor": false,
|
||||
"saveundo": false,
|
||||
@@ -254,8 +258,6 @@ func DefaultLocalSettings() map[string]interface{} {
|
||||
"tabsize": float64(4),
|
||||
"tabstospaces": false,
|
||||
"useprimary": true,
|
||||
"fileformat": "unix",
|
||||
"mouse": true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,6 +317,14 @@ func SetOption(option, value string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if option == "mouse" {
|
||||
if !nativeValue.(bool) {
|
||||
screen.DisableMouse()
|
||||
} else {
|
||||
screen.EnableMouse()
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := CurView().Buf.Settings[option]; ok {
|
||||
for _, tab := range tabs {
|
||||
for _, view := range tab.views {
|
||||
@@ -358,6 +368,26 @@ func SetLocalOption(option, value string, view *View) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if option == "fastdirty" {
|
||||
// If it is being turned off, we have to hash every open buffer
|
||||
var empty [16]byte
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
v.Buf.IsModified = v.Buf.Modified()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.Settings[option] = nativeValue
|
||||
|
||||
if option == "statusline" {
|
||||
@@ -382,14 +412,6 @@ func SetLocalOption(option, value string, view *View) error {
|
||||
}
|
||||
}
|
||||
|
||||
if option == "mouse" {
|
||||
if !nativeValue.(bool) {
|
||||
screen.DisableMouse()
|
||||
} else {
|
||||
screen.EnableMouse()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ func (sline *Statusline) Display() {
|
||||
file := sline.view.Buf.GetName()
|
||||
|
||||
// If the buffer is dirty (has been modified) write a little '+'
|
||||
if sline.view.Buf.IsModified {
|
||||
if sline.view.Buf.Modified() {
|
||||
file += " +"
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ func TabbarString() (string, map[int]int) {
|
||||
}
|
||||
buf := t.views[t.CurView].Buf
|
||||
str += buf.GetName()
|
||||
if buf.IsModified {
|
||||
if buf.Modified() {
|
||||
str += " +"
|
||||
}
|
||||
if i == curTab {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// Util.go is a collection of utility functions that are used throughout
|
||||
@@ -363,3 +364,18 @@ func JoinCommandArgs(args ...string) string {
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// ReplaceHome takes a path as input and replaces ~ at the start of the path with the user's
|
||||
// home directory. Does nothing if the path does not start with '~'.
|
||||
func ReplaceHome(path string) string {
|
||||
if !strings.HasPrefix(path, "~") {
|
||||
return path
|
||||
}
|
||||
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
messenger.Error("Could not find home directory: ", err)
|
||||
return path
|
||||
}
|
||||
return strings.Replace(path, "~", home, 1)
|
||||
}
|
||||
|
||||
2
cmd/micro/vendor/github.com/zyedidia/tcell
generated
vendored
2
cmd/micro/vendor/github.com/zyedidia/tcell
generated
vendored
Submodule cmd/micro/vendor/github.com/zyedidia/tcell updated: 37b78458fe...c47e75564a
@@ -6,7 +6,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/zyedidia/tcell"
|
||||
)
|
||||
|
||||
@@ -73,6 +72,7 @@ type View struct {
|
||||
// This stores when the last click was
|
||||
// This is useful for detecting double and triple clicks
|
||||
lastClickTime time.Time
|
||||
lastLoc Loc
|
||||
|
||||
// lastCutTime stores when the last ctrl+k was issued.
|
||||
// It is used for clearing the clipboard to replace it with fresh cut lines.
|
||||
@@ -91,9 +91,6 @@ type View struct {
|
||||
cellview *CellView
|
||||
|
||||
splitNode *LeafNode
|
||||
|
||||
resizeX bool
|
||||
resizeY bool
|
||||
}
|
||||
|
||||
// NewView returns a new fullscreen view
|
||||
@@ -201,7 +198,7 @@ func (v *View) ScrollDown(n int) {
|
||||
// If there are unsaved changes, the user will be asked if the view can be closed
|
||||
// causing them to lose the unsaved changes
|
||||
func (v *View) CanClose() bool {
|
||||
if v.Type == vtDefault && v.Buf.IsModified {
|
||||
if v.Type == vtDefault && v.Buf.Modified() {
|
||||
var choice bool
|
||||
var canceled bool
|
||||
if v.Buf.Settings["autosave"].(bool) {
|
||||
@@ -246,8 +243,7 @@ func (v *View) OpenBuffer(buf *Buffer) {
|
||||
|
||||
// Open opens the given file in the view
|
||||
func (v *View) Open(filename string) {
|
||||
home, _ := homedir.Dir()
|
||||
filename = strings.Replace(filename, "~", home, 1)
|
||||
filename = ReplaceHome(filename)
|
||||
file, err := os.Open(filename)
|
||||
fileInfo, _ := os.Stat(filename)
|
||||
|
||||
@@ -602,26 +598,21 @@ func (v *View) HandleEvent(event tcell.Event) {
|
||||
// Mouse event with no click
|
||||
if !v.mouseReleased {
|
||||
// Mouse was just released
|
||||
if v.resizeX || v.resizeY {
|
||||
v.resizeX = false
|
||||
v.resizeY = false
|
||||
} else {
|
||||
|
||||
x, y := e.Position()
|
||||
x -= v.lineNumOffset - v.leftCol + v.x
|
||||
y += v.Topline - v.y
|
||||
x, y := e.Position()
|
||||
x -= v.lineNumOffset - v.leftCol + v.x
|
||||
y += v.Topline - v.y
|
||||
|
||||
// Relocating here isn't really necessary because the cursor will
|
||||
// be in the right place from the last mouse event
|
||||
// However, if we are running in a terminal that doesn't support mouse motion
|
||||
// events, this still allows the user to make selections, except only after they
|
||||
// release the mouse
|
||||
// Relocating here isn't really necessary because the cursor will
|
||||
// be in the right place from the last mouse event
|
||||
// However, if we are running in a terminal that doesn't support mouse motion
|
||||
// events, this still allows the user to make selections, except only after they
|
||||
// release the mouse
|
||||
|
||||
if !v.doubleClick && !v.tripleClick {
|
||||
v.MoveToMouseClick(x, y)
|
||||
v.Cursor.SetSelectionEnd(v.Cursor.Loc)
|
||||
v.Cursor.CopySelection("primary")
|
||||
}
|
||||
if !v.doubleClick && !v.tripleClick {
|
||||
v.MoveToMouseClick(x, y)
|
||||
v.Cursor.SetSelectionEnd(v.Cursor.Loc)
|
||||
v.Cursor.CopySelection("primary")
|
||||
}
|
||||
v.mouseReleased = true
|
||||
}
|
||||
|
||||
@@ -161,6 +161,22 @@ Here are the options that you can set:
|
||||
|
||||
default value: `on`
|
||||
|
||||
* `fileformat`: this determines what kind of line endings micro will use for the file. Unix line endings
|
||||
are just `\n` (lf) whereas dos line endings are `\r\n` (crlf). The two possible values for this option
|
||||
are `unix` and `dos`. The fileformat will be automatically detected and displayed on the statusline but
|
||||
this option is useful if you would like to change the line endings or if you are starting a new file.
|
||||
|
||||
default value: `unix`
|
||||
|
||||
* `fastdirty`: this determines what kind of algorithm micro uses to determine if a buffer is modified or
|
||||
not. When `fastdirty` is on, micro just uses a boolean `modified` that is set to `true` as soon as the user
|
||||
makes an edit. This is fast, but can be inaccurate. If `fastdirty` is off, then micro will hash the current
|
||||
buffer against a hash of the original file (created when the buffer was loaded). This is more accurate but
|
||||
obviously more resource intensive. This option is only for people who really care about having accurate
|
||||
modified status.
|
||||
|
||||
default value: `on`
|
||||
|
||||
---
|
||||
|
||||
Default plugin options:
|
||||
@@ -179,13 +195,6 @@ Default plugin options:
|
||||
|
||||
default value: `on`
|
||||
|
||||
* `fileformat`: this determines what kind of line endings micro will use for the file. Unix line endings
|
||||
are just `\n` (lf) whereas dos line endings are `\r\n` (crlf). The two possible values for this option
|
||||
are `unix` and `dos`. The fileformat will be automatically detected and displayed on the statusline but
|
||||
this option is useful if you would like to change the line endings or if you are starting a new file.
|
||||
|
||||
default value: `unix`
|
||||
|
||||
Any option you set in the editor will be saved to the file
|
||||
~/.config/micro/settings.json so, in effect, your configuration file will be
|
||||
created for you. If you'd like to take your configuration with you to another
|
||||
|
||||
@@ -5,7 +5,7 @@ detect:
|
||||
|
||||
rules:
|
||||
## Keywords
|
||||
- keyword: "(?i)^(FROM|MAINTAINER|RUN|CMD|LABEL|EXPOSE|ENV|ADD|COPY|ENTRYPOINT|VOLUME|USER|WORKDIR|ONBUILD|ARG|HEALTHCHECK|STOPSIGNAL|SHELL)[[:space:]]"
|
||||
- type.keyword: "(?i)^(FROM|MAINTAINER|RUN|CMD|LABEL|EXPOSE|ENV|ADD|COPY|ENTRYPOINT|VOLUME|USER|WORKDIR|ONBUILD|ARG|HEALTHCHECK|STOPSIGNAL|SHELL)[[:space:]]"
|
||||
|
||||
## Brackets & parenthesis
|
||||
- statement: "(\\(|\\)|\\[|\\])"
|
||||
|
||||
@@ -12,17 +12,18 @@ rules:
|
||||
end: "$"
|
||||
rules: []
|
||||
# File changes
|
||||
- keyword: "#[[:space:]](deleted|modified|new file|renamed):[[:space:]].*"
|
||||
- keyword: "#[[:space:]]deleted:"
|
||||
- keyword: "#[[:space:]]modified:"
|
||||
- keyword: "#[[:space:]]new file:"
|
||||
- keyword: "#[[:space:]]renamed:"
|
||||
- type.keyword: "#[[:space:]](deleted|modified|new file|renamed):[[:space:]].*"
|
||||
- type.keyword: "#[[:space:]]deleted:"
|
||||
- type.keyword: "#[[:space:]]modified:"
|
||||
- type.keyword: "#[[:space:]]new file:"
|
||||
- type.keyword: "#[[:space:]]renamed:"
|
||||
# Untracked filenames
|
||||
- error: "^# [^/?*:;{}\\\\]+\\.[^/?*:;{}\\\\]+$"
|
||||
- keyword: "^#[[:space:]]Changes.*[:]"
|
||||
- keyword: "^#[[:space:]]Your branch and '[^']+"
|
||||
- keyword: "^#[[:space:]]Your branch and '"
|
||||
- keyword: "^#[[:space:]]On branch [^ ]+"
|
||||
- keyword: "^#[[:space:]]On branch"
|
||||
- type.keyword: "^#[[:space:]]Changes.*[:]"
|
||||
- type.keyword: "^#[[:space:]]Your branch and '[^']+"
|
||||
- type.keyword: "^#[[:space:]]Your branch and '"
|
||||
- type.keyword: "^#[[:space:]]On branch [^ ]+"
|
||||
- type.keyword: "^#[[:space:]]On branch"
|
||||
# Recolor hash symbols
|
||||
- special: "#"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ detect:
|
||||
|
||||
rules:
|
||||
- constant: "\\<(true|false)\\>"
|
||||
- keyword: "^[[:space:]]*[^=]*="
|
||||
- type.keyword: "^[[:space:]]*[^=]*="
|
||||
- constant: "^[[:space:]]*\\[.*\\]$"
|
||||
- constant: "\"(\\\\.|[^\"])*\"|'(\\\\.|[^'])*'"
|
||||
- comment:
|
||||
|
||||
@@ -7,21 +7,33 @@ detect:
|
||||
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))\\b|\\b(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)\\b|\\b(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(dir)?)\\b|\\b(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)\\b|\\b(un(def|link|pack|shift)|utime|values|vec|wait(pid)?|wantarray|warn|write)\\b"
|
||||
- statement: "\\b(continue|else|elsif|do|for|foreach|if|unless|until|while|eq|ne|lt|gt|le|ge|cmp|x|my|sub|use|package|can|isa)\\b"
|
||||
|
||||
- special: "\\-\\>"
|
||||
- symbol: "(,|\\.)"
|
||||
|
||||
- identifier:
|
||||
start: "[$@%]"
|
||||
end: "((?i) |[^0-9A-Z_]|-)"
|
||||
start: "[\\$@%]"
|
||||
end: "\\W"
|
||||
rules: []
|
||||
|
||||
- constant.string: "\".*\"|qq\\|.*\\|"
|
||||
- constant.string: "\"\\(.*\\)\"|qq?\\|.*\\||qq?\\{.*\\}|qq?\\/.*\\/"
|
||||
- default: "[sm]/.*/"
|
||||
- preproc:
|
||||
start: "(^use| = new)"
|
||||
end: ";"
|
||||
rules: []
|
||||
|
||||
- comment: "#.*"
|
||||
- comment:
|
||||
start: "#"
|
||||
end: "$"
|
||||
rules: []
|
||||
|
||||
- comment:
|
||||
start: "^="
|
||||
end: "^=cut"
|
||||
rules: []
|
||||
|
||||
- identifier.macro:
|
||||
start: "<< 'STOP'"
|
||||
end: "STOP"
|
||||
rules: []
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ rules:
|
||||
- identifier: "[a-zA-Z][_a-zA-Z0-9]*[[:space:]]*"
|
||||
- statement: "\\b(assembly|break|continue|do|for|function|if|else|new|return|returns|while)\\b"
|
||||
- special: "\\b(\\.send|throw)\\b" # make sure they are very visible
|
||||
- keyword: "\\b(anonymous|constant|indexed|payable|public|private|external|internal)\\b"
|
||||
- type.keyword: "\\b(anonymous|constant|indexed|payable|public|private|external|internal)\\b"
|
||||
- constant: "\\b(block(\\.(blockhash|coinbase|difficulty|gaslimit|number|timestamp))?|msg(\\.(data|gas|sender|value))?|now|tx(\\.(gasprice|origin))?)\\b"
|
||||
- constant: "\\b(keccak256|sha3|sha256|ripemd160|ecrecover|addmod|mulmod|this|super|selfdestruct|\\.balance)\\b"
|
||||
- constant: "\\b(true|false)\\b"
|
||||
|
||||
Reference in New Issue
Block a user