diff --git a/cmd/micro/action/actions.go b/cmd/micro/action/actions.go index d280c333..bf803f40 100644 --- a/cmd/micro/action/actions.go +++ b/cmd/micro/action/actions.go @@ -1009,6 +1009,7 @@ func (h *BufHandler) Escape() bool { // Quit this will close the current tab or view that is open func (h *BufHandler) Quit() bool { quit := func() { + h.Buf.Close() if len(MainTab().Panes) > 1 { h.Unsplit() } else if len(Tabs.List) > 1 { diff --git a/cmd/micro/action/actions_posix.go b/cmd/micro/action/actions_posix.go index e91ea256..c6ac2b0d 100644 --- a/cmd/micro/action/actions_posix.go +++ b/cmd/micro/action/actions_posix.go @@ -6,7 +6,6 @@ import ( "syscall" "github.com/zyedidia/micro/cmd/micro/screen" - "github.com/zyedidia/micro/cmd/micro/util" ) // Suspend sends micro to the background. This is the same as pressing CtrlZ in most unix programs. @@ -19,7 +18,7 @@ func (*BufHandler) Suspend() bool { pid := syscall.Getpid() err := syscall.Kill(pid, syscall.SIGSTOP) if err != nil { - util.TermMessage(err) + screen.TermMessage(err) } screen.TempStart(screenb) diff --git a/cmd/micro/action/bindings.go b/cmd/micro/action/bindings.go index ea097f81..b2aa26f0 100644 --- a/cmd/micro/action/bindings.go +++ b/cmd/micro/action/bindings.go @@ -10,7 +10,7 @@ import ( "github.com/flynn/json5" "github.com/zyedidia/micro/cmd/micro/config" - "github.com/zyedidia/micro/cmd/micro/util" + "github.com/zyedidia/micro/cmd/micro/screen" "github.com/zyedidia/tcell" ) @@ -24,13 +24,13 @@ func InitBindings() { if _, e := os.Stat(filename); e == nil { input, err := ioutil.ReadFile(filename) if err != nil { - util.TermMessage("Error reading bindings.json file: " + err.Error()) + screen.TermMessage("Error reading bindings.json file: " + err.Error()) return } err = json5.Unmarshal(input, &parsed) if err != nil { - util.TermMessage("Error reading bindings.json:", err.Error()) + screen.TermMessage("Error reading bindings.json:", err.Error()) } } @@ -45,7 +45,7 @@ func InitBindings() { func BindKey(k, v string) { event, ok := findEvent(k) if !ok { - util.TermMessage(k, "is not a bindable event") + screen.TermMessage(k, "is not a bindable event") } switch e := event.(type) { diff --git a/cmd/micro/action/bufhandler.go b/cmd/micro/action/bufhandler.go index 0ffa514f..54d65374 100644 --- a/cmd/micro/action/bufhandler.go +++ b/cmd/micro/action/bufhandler.go @@ -5,7 +5,7 @@ import ( "github.com/zyedidia/micro/cmd/micro/buffer" "github.com/zyedidia/micro/cmd/micro/display" - "github.com/zyedidia/micro/cmd/micro/util" + "github.com/zyedidia/micro/cmd/micro/screen" "github.com/zyedidia/tcell" ) @@ -28,7 +28,7 @@ func BufMapKey(k Event, action string) { BufKeyStrings[k] = action BufKeyBindings[k] = f } else { - util.TermMessage("Error:", action, "does not exist") + screen.TermMessage("Error:", action, "does not exist") } } @@ -43,7 +43,7 @@ func BufMapMouse(k MouseEvent, action string) { // ensure we don't double bind a key delete(BufMouseBindings, k) } else { - util.TermMessage("Error:", action, "does not exist") + screen.TermMessage("Error:", action, "does not exist") } } @@ -268,6 +268,9 @@ func (h *BufHandler) HSplitBuf(buf *buffer.Buffer) { MainTab().Resize() MainTab().SetActive(len(MainTab().Panes) - 1) } +func (h *BufHandler) Close() { + h.Buf.Close() +} // BufKeyActions contains the list of all possible key actions the bufhandler could execute var BufKeyActions = map[string]BufKeyAction{ diff --git a/cmd/micro/action/command.go b/cmd/micro/action/command.go index 814c99cb..e4525ab0 100644 --- a/cmd/micro/action/command.go +++ b/cmd/micro/action/command.go @@ -257,12 +257,86 @@ func (h *BufHandler) NewTabCmd(args []string) { } } +func SetGlobalOption(option, value string) error { + if _, ok := config.GlobalSettings[option]; !ok { + return config.ErrInvalidOption + } + + nativeValue, err := config.GetNativeValue(option, config.GlobalSettings[option], value) + if err != nil { + return err + } + + config.GlobalSettings[option] = nativeValue + + if option == "colorscheme" { + // LoadSyntaxFiles() + config.InitColorscheme() + for _, b := range buffer.OpenBuffers { + b.UpdateRules() + } + } + + // TODO: info and keymenu option change + // if option == "infobar" || option == "keymenu" { + // for _, tab := range tabs { + // tab.Resize() + // } + // } + + if option == "mouse" { + if !nativeValue.(bool) { + screen.Screen.DisableMouse() + } else { + screen.Screen.EnableMouse() + } + } + + for _, b := range buffer.OpenBuffers { + b.SetOption(option, value) + } + + config.WriteSettings(config.ConfigDir + "/settings.json") + + return nil +} + // SetCmd sets an option func (h *BufHandler) SetCmd(args []string) { + if len(args) < 2 { + InfoBar.Error("Not enough arguments") + return + } + + option := args[0] + value := args[1] + + err := SetGlobalOption(option, value) + if err == config.ErrInvalidOption { + err := h.Buf.SetOption(option, value) + if err != nil { + InfoBar.Error(err) + } + } else if err != nil { + InfoBar.Error(err) + } } // SetLocalCmd sets an option local to the buffer func (h *BufHandler) SetLocalCmd(args []string) { + if len(args) < 2 { + InfoBar.Error("Not enough arguments") + return + } + + option := args[0] + value := args[1] + + err := h.Buf.SetOption(option, value) + if err != nil { + InfoBar.Error(err) + } + } // ShowCmd shows the value of the given option @@ -341,6 +415,8 @@ func (h *BufHandler) TermCmd(args []string) { h.AddTab() i = 0 id = MainTab().Panes[0].ID() + } else { + MainTab().Panes[i].Close() } v := h.GetView() diff --git a/cmd/micro/action/editpane.go b/cmd/micro/action/pane.go similarity index 98% rename from cmd/micro/action/editpane.go rename to cmd/micro/action/pane.go index dfc2d78f..f7db4b68 100644 --- a/cmd/micro/action/editpane.go +++ b/cmd/micro/action/pane.go @@ -11,6 +11,7 @@ type Pane interface { display.Window ID() uint64 Name() string + Close() } type EditPane struct { diff --git a/cmd/micro/action/termhandler.go b/cmd/micro/action/termhandler.go index 184daf76..0d19c551 100644 --- a/cmd/micro/action/termhandler.go +++ b/cmd/micro/action/termhandler.go @@ -31,7 +31,10 @@ func (t *TermHandler) ID() uint64 { return t.id } +func (t *TermHandler) Close() {} + func (t *TermHandler) Quit() { + t.Close() if len(MainTab().Panes) > 1 { t.Unsplit() } else if len(Tabs.List) > 1 { diff --git a/cmd/micro/buffer/buffer.go b/cmd/micro/buffer/buffer.go index 96b76dd1..8ee1a6bb 100644 --- a/cmd/micro/buffer/buffer.go +++ b/cmd/micro/buffer/buffer.go @@ -1,7 +1,6 @@ package buffer import ( - "bufio" "crypto/md5" "errors" "io" @@ -14,39 +13,11 @@ import ( "github.com/zyedidia/micro/cmd/micro/config" "github.com/zyedidia/micro/cmd/micro/highlight" - + "github.com/zyedidia/micro/cmd/micro/screen" . "github.com/zyedidia/micro/cmd/micro/util" ) -// LargeFileThreshold is the number of bytes when fastdirty is forced -// because hashing is too slow -const LargeFileThreshold = 50000 - -// 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 -} +var OpenBuffers []*Buffer // The BufType defines what kind of buffer this is type BufType struct { @@ -63,6 +34,8 @@ var ( BTScratch = BufType{3, false, true, false} BTRaw = BufType{4, true, true, false} BTInfo = BufType{5, false, true, false} + + ErrFileTooLarge = errors.New("File is too large to hash") ) // Buffer stores the main information about a currently open file including @@ -149,6 +122,8 @@ func NewBufferFromString(text, path string, btype BufType) *Buffer { // Ensure that ReadSettings and InitGlobalSettings have been called before creating // a new buffer func NewBuffer(reader io.Reader, size int64, path string, cursorPosition []string, btype BufType) *Buffer { + absPath, _ := filepath.Abs(path) + b := new(Buffer) b.Type = btype @@ -162,8 +137,6 @@ func NewBuffer(reader io.Reader, size int64, path string, cursorPosition []strin b.LineArray = NewLineArray(uint64(size), FFAuto, reader) - absPath, _ := filepath.Abs(path) - b.Path = path b.AbsPath = absPath @@ -187,7 +160,7 @@ func NewBuffer(reader io.Reader, size int64, path string, cursorPosition []strin if b.Settings["savecursor"].(bool) || b.Settings["saveundo"].(bool) { err := b.Unserialize() if err != nil { - TermMessage(err) + screen.TermMessage(err) } } @@ -200,9 +173,23 @@ func NewBuffer(reader io.Reader, size int64, path string, cursorPosition []strin } } + OpenBuffers = append(OpenBuffers, b) + return b } +// Close removes this buffer from the list of open buffers +func (b *Buffer) Close() { + for i, buf := range OpenBuffers { + if b == buf { + copy(OpenBuffers[i:], OpenBuffers[i+1:]) + OpenBuffers[len(OpenBuffers)-1] = nil + OpenBuffers = OpenBuffers[:len(OpenBuffers)-1] + return + } + } +} + // GetName returns the name that should be displayed in the statusline // for this buffer func (b *Buffer) GetName() string { @@ -294,19 +281,37 @@ func (b *Buffer) Modified() bool { } // calcHash calculates md5 hash of all lines in the buffer -func calcHash(b *Buffer, out *[md5.Size]byte) { +func calcHash(b *Buffer, out *[md5.Size]byte) error { h := md5.New() + size := 0 if len(b.lines) > 0 { - h.Write(b.lines[0].data) + n, e := h.Write(b.lines[0].data) + if e != nil { + return e + } + size += n for _, l := range b.lines[1:] { - h.Write([]byte{'\n'}) - h.Write(l.data) + n, e = h.Write([]byte{'\n'}) + if e != nil { + return e + } + size += n + n, e = h.Write(l.data) + if e != nil { + return e + } + size += n } } + if size > LargeFileThreshold { + return ErrFileTooLarge + } + h.Sum((*out)[:0]) + return nil } func (b *Buffer) insert(pos Loc, value []byte) { @@ -334,16 +339,16 @@ func (b *Buffer) UpdateRules() { for _, f := range config.ListRuntimeFiles(config.RTSyntax) { data, err := f.Data() if err != nil { - TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) + screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) } else { file, err := highlight.ParseFile(data) if err != nil { - TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) + screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) continue } ftdetect, err := highlight.ParseFtDetect(file) if err != nil { - TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) + screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) continue } @@ -355,7 +360,7 @@ func (b *Buffer) UpdateRules() { header.FtDetect = ftdetect b.SyntaxDef, err = highlight.ParseDef(file, header) if err != nil { - TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) + screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) continue } rehighlight = true @@ -367,7 +372,7 @@ func (b *Buffer) UpdateRules() { header.FtDetect = ftdetect b.SyntaxDef, err = highlight.ParseDef(file, header) if err != nil { - TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) + screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error()) continue } rehighlight = true @@ -392,6 +397,14 @@ func (b *Buffer) UpdateRules() { } } +// ClearMatches clears all of the syntax highlighting for the buffer +func (b *Buffer) ClearMatches() { + for i := range b.lines { + b.SetMatch(i, nil) + b.SetState(i, nil) + } +} + // IndentString returns this buffer's indent method (a tabstop or n spaces // depending on the settings) func (b *Buffer) IndentString(tabsize int) string { diff --git a/cmd/micro/buffer/eventhandler.go b/cmd/micro/buffer/eventhandler.go index 00d2c865..e6bd37a1 100644 --- a/cmd/micro/buffer/eventhandler.go +++ b/cmd/micro/buffer/eventhandler.go @@ -188,7 +188,7 @@ func (eh *EventHandler) Execute(t *TextEvent) { // for pl := range loadedPlugins { // ret, err := Call(pl+".onBeforeTextEvent", t) // if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") { - // util.TermMessage(err) + // screen.TermMessage(err) // } // if val, ok := ret.(lua.LBool); ok && val == lua.LFalse { // return diff --git a/cmd/micro/buffer/save.go b/cmd/micro/buffer/save.go index a2270434..fe82a8e0 100644 --- a/cmd/micro/buffer/save.go +++ b/cmd/micro/buffer/save.go @@ -1,6 +1,7 @@ package buffer import ( + "bufio" "bytes" "io" "os" @@ -8,11 +9,40 @@ import ( "os/signal" "path/filepath" - . "github.com/zyedidia/micro/cmd/micro/util" - "github.com/zyedidia/micro/cmd/micro/config" + . "github.com/zyedidia/micro/cmd/micro/util" ) +// LargeFileThreshold is the number of bytes when fastdirty is forced +// because hashing is too slow +const LargeFileThreshold = 50000 + +// 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 +} + // Save saves the buffer to its default path func (b *Buffer) Save() error { return b.SaveAs(b.Path) diff --git a/cmd/micro/buffer/settings.go b/cmd/micro/buffer/settings.go new file mode 100644 index 00000000..fb3b5486 --- /dev/null +++ b/cmd/micro/buffer/settings.go @@ -0,0 +1,43 @@ +package buffer + +import ( + "github.com/zyedidia/micro/cmd/micro/config" + "github.com/zyedidia/micro/cmd/micro/screen" +) + +// SetOption sets a given option to a value just for this buffer +func (b *Buffer) SetOption(option, value string) error { + if _, ok := b.Settings[option]; !ok { + return config.ErrInvalidOption + } + + nativeValue, err := config.GetNativeValue(option, b.Settings[option], value) + if err != nil { + return err + } + + b.Settings[option] = nativeValue + + if option == "fastdirty" { + if !nativeValue.(bool) { + e := calcHash(b, &b.origHash) + if e == ErrFileTooLarge { + b.Settings["fastdirty"] = false + } + } + } else if option == "statusline" { + screen.Redraw() + } else if option == "filetype" { + b.UpdateRules() + } else if option == "fileformat" { + b.isModified = true + } else if option == "syntax" { + if !nativeValue.(bool) { + b.ClearMatches() + } else { + b.UpdateRules() + } + } + + return nil +} diff --git a/cmd/micro/config/settings.go b/cmd/micro/config/settings.go index 2536b857..b82bd78f 100644 --- a/cmd/micro/config/settings.go +++ b/cmd/micro/config/settings.go @@ -6,19 +6,26 @@ import ( "io/ioutil" "os" "reflect" + "strconv" "strings" "github.com/flynn/json5" "github.com/zyedidia/glob" + "github.com/zyedidia/micro/cmd/micro/util" ) type optionValidator func(string, interface{}) error -// The options that the user can set -var GlobalSettings map[string]interface{} +var ( + ErrInvalidOption = errors.New("Invalid option") + ErrInvalidValue = errors.New("Invalid value") -// This is the raw parsed json -var parsedSettings map[string]interface{} + // The options that the user can set + GlobalSettings map[string]interface{} + + // This is the raw parsed json + parsedSettings map[string]interface{} +) // Options with validators var optionValidators = map[string]optionValidator{ @@ -120,22 +127,6 @@ func GetGlobalOption(name string) interface{} { return GlobalSettings[name] } -// GetLocalOption returns the local value of the given option -// func GetLocalOption(name string, buf *Buffer) interface{} { -// return buf.Settings[name] -// } - -// TODO: get option for current buffer -// GetOption returns the value of the given option -// If there is a local version of the option, it returns that -// otherwise it will return the global version -// func GetOption(name string) interface{} { -// if GetLocalOption(name, CurView().Buf) != nil { -// return GetLocalOption(name, CurView().Buf) -// } -// return GetGlobalOption(name) -// } - func DefaultCommonSettings() map[string]interface{} { return map[string]interface{}{ "autoindent": true, @@ -196,199 +187,35 @@ func DefaultLocalSettings() map[string]interface{} { return common } -// TODO: everything else +func GetNativeValue(option string, realValue interface{}, value string) (interface{}, error) { + var native interface{} + kind := reflect.TypeOf(realValue).Kind() + if kind == reflect.Bool { + b, err := util.ParseBool(value) + if err != nil { + return nil, ErrInvalidValue + } + native = b + } else if kind == reflect.String { + native = value + } else if kind == reflect.Float64 { + i, err := strconv.Atoi(value) + if err != nil { + return nil, ErrInvalidValue + } + native = float64(i) + } else { + return nil, ErrInvalidValue + } -// SetOption attempts to set the given option to the value -// By default it will set the option as global, but if the option -// is local only it will set the local version -// Use setlocal to force an option to be set locally -// func SetOption(option, value string) error { -// if _, ok := GlobalSettings[option]; !ok { -// if _, ok := CurView().Buf.Settings[option]; !ok { -// return errors.New("Invalid option") -// } -// SetLocalOption(option, value, CurView()) -// return nil -// } -// -// var nativeValue interface{} -// -// kind := reflect.TypeOf(GlobalSettings[option]).Kind() -// if kind == reflect.Bool { -// b, err := ParseBool(value) -// if err != nil { -// return errors.New("Invalid value") -// } -// nativeValue = b -// } else if kind == reflect.String { -// nativeValue = value -// } else if kind == reflect.Float64 { -// i, err := strconv.Atoi(value) -// if err != nil { -// return errors.New("Invalid value") -// } -// nativeValue = float64(i) -// } else { -// return errors.New("Option has unsupported value type") -// } -// -// if err := optionIsValid(option, nativeValue); err != nil { -// return err -// } -// -// GlobalSettings[option] = nativeValue -// -// if option == "colorscheme" { -// // LoadSyntaxFiles() -// InitColorscheme() -// for _, tab := range tabs { -// for _, view := range tab.Views { -// view.Buf.UpdateRules() -// } -// } -// } -// -// if option == "infobar" || option == "keymenu" { -// for _, tab := range tabs { -// tab.Resize() -// } -// } -// -// if option == "mouse" { -// if !nativeValue.(bool) { -// screen.DisableMouse() -// } else { -// screen.EnableMouse() -// } -// } -// -// if len(tabs) != 0 { -// if _, ok := CurView().Buf.Settings[option]; ok { -// for _, tab := range tabs { -// for _, view := range tab.Views { -// SetLocalOption(option, value, view) -// } -// } -// } -// } -// -// return nil -// } -// -// // SetLocalOption sets the local version of this option -// func SetLocalOption(option, value string, view *View) error { -// buf := view.Buf -// if _, ok := buf.Settings[option]; !ok { -// return errors.New("Invalid option") -// } -// -// var nativeValue interface{} -// -// kind := reflect.TypeOf(buf.Settings[option]).Kind() -// if kind == reflect.Bool { -// b, err := ParseBool(value) -// if err != nil { -// return errors.New("Invalid value") -// } -// nativeValue = b -// } else if kind == reflect.String { -// nativeValue = value -// } else if kind == reflect.Float64 { -// i, err := strconv.Atoi(value) -// if err != nil { -// return errors.New("Invalid value") -// } -// nativeValue = float64(i) -// } else { -// return errors.New("Option has unsupported value type") -// } -// -// if err := optionIsValid(option, nativeValue); err != nil { -// return err -// } -// -// if option == "fastdirty" { -// // If it is being turned off, we have to hash every open buffer -// 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 { -// 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 -// -// if option == "statusline" { -// view.ToggleStatusLine() -// } -// -// if option == "filetype" { -// // LoadSyntaxFiles() -// InitColorscheme() -// buf.UpdateRules() -// } -// -// if option == "fileformat" { -// buf.IsModified = true -// } -// -// if option == "syntax" { -// if !nativeValue.(bool) { -// buf.ClearMatches() -// } else { -// if buf.highlighter != nil { -// buf.highlighter.HighlightStates(buf) -// } -// } -// } -// -// return nil -// } -// -// // SetOptionAndSettings sets the given option and saves the option setting to the settings config file -// func SetOptionAndSettings(option, value string) { -// filename := ConfigDir + "/settings.json" -// -// err := SetOption(option, value) -// -// if err != nil { -// messenger.Error(err.Error()) -// return -// } -// -// err = WriteSettings(filename) -// if err != nil { -// messenger.Error("Error writing to settings.json: " + err.Error()) -// return -// } -// } + if err := OptionIsValid(option, native); err != nil { + return nil, err + } + return native, nil +} -func optionIsValid(option string, value interface{}) error { +// OptionIsValid checks if a value is valid for a certain option +func OptionIsValid(option string, value interface{}) error { if validator, ok := optionValidators[option]; ok { return validator(option, value) } diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index 52507427..a0c1d860 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -13,7 +13,6 @@ import ( "github.com/zyedidia/micro/cmd/micro/buffer" "github.com/zyedidia/micro/cmd/micro/config" "github.com/zyedidia/micro/cmd/micro/screen" - "github.com/zyedidia/micro/cmd/micro/util" "github.com/zyedidia/tcell" ) @@ -123,7 +122,7 @@ func LoadInput() []*buffer.Buffer { buf, err := buffer.NewBufferFromFile(args[i], buffer.BTDefault) if err != nil { - util.TermMessage(err) + screen.TermMessage(err) continue } // If the file didn't exist, input will be empty, and we'll open an empty buffer @@ -135,7 +134,7 @@ func LoadInput() []*buffer.Buffer { // and we should read from stdin input, err = ioutil.ReadAll(os.Stdin) if err != nil { - util.TermMessage("Error reading from stdin: ", err) + screen.TermMessage("Error reading from stdin: ", err) input = []byte{} } buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, buffer.BTDefault)) @@ -154,12 +153,12 @@ func main() { InitFlags() err = config.InitConfigDir(*flagConfigDir) if err != nil { - util.TermMessage(err) + screen.TermMessage(err) } config.InitRuntimeFiles() err = config.ReadSettings() if err != nil { - util.TermMessage(err) + screen.TermMessage(err) } config.InitGlobalSettings() action.InitBindings() @@ -167,7 +166,7 @@ func main() { err = config.InitColorscheme() if err != nil { - util.TermMessage(err) + screen.TermMessage(err) } screen.Init() diff --git a/cmd/micro/util/message.go b/cmd/micro/screen/message.go similarity index 87% rename from cmd/micro/util/message.go rename to cmd/micro/screen/message.go index efaf059d..cd4abce9 100644 --- a/cmd/micro/util/message.go +++ b/cmd/micro/screen/message.go @@ -1,12 +1,10 @@ -package util +package screen import ( "bufio" "fmt" "os" "strconv" - - "github.com/zyedidia/micro/cmd/micro/screen" ) // TermMessage sends a message to the user in the terminal. This usually occurs before @@ -16,7 +14,7 @@ import ( // This will write the message, and wait for the user // to press and key to continue func TermMessage(msg ...interface{}) { - screenb := screen.TempFini() + screenb := TempFini() fmt.Println(msg...) fmt.Print("\nPress enter to continue") @@ -24,7 +22,7 @@ func TermMessage(msg ...interface{}) { reader := bufio.NewReader(os.Stdin) reader.ReadString('\n') - screen.TempStart(screenb) + TempStart(screenb) } // TermError sends an error to the user in the terminal. Like TermMessage except formatted diff --git a/cmd/micro/shell/shell.go b/cmd/micro/shell/shell.go index 9c57d616..7087cfe4 100644 --- a/cmd/micro/shell/shell.go +++ b/cmd/micro/shell/shell.go @@ -11,7 +11,6 @@ import ( "github.com/zyedidia/micro/cmd/micro/screen" "github.com/zyedidia/micro/cmd/micro/shellwords" - "github.com/zyedidia/micro/cmd/micro/util" ) // ExecCommand executes a command using exec @@ -108,7 +107,7 @@ func RunInteractiveShell(input string, wait bool, getOutput bool) (string, error if wait { // This is just so we don't return right away and let the user press enter to return - util.TermMessage("") + screen.TermMessage("") } // Start the screen back up diff --git a/cmd/micro/util/util.go b/cmd/micro/util/util.go index f64305a9..bec680f1 100644 --- a/cmd/micro/util/util.go +++ b/cmd/micro/util/util.go @@ -6,6 +6,7 @@ import ( "os/user" "path/filepath" "regexp" + "strconv" "strings" "time" "unicode/utf8" @@ -309,6 +310,19 @@ func GetCharPosInLine(b []byte, visualPos int, tabsize int) int { return i } +// ParseBool is almost exactly like strconv.ParseBool, except it also accepts 'on' and 'off' +// as 'true' and 'false' respectively +func ParseBool(str string) (bool, error) { + if str == "on" { + return true, nil + } + if str == "off" { + return false, nil + } + return strconv.ParseBool(str) +} + +// Clamp clamps a value between min and max func Clamp(val, min, max int) int { if val < min { val = min