diff --git a/src/buffer.go b/src/buffer.go index c96ff385..2020babe 100644 --- a/src/buffer.go +++ b/src/buffer.go @@ -41,12 +41,17 @@ func NewBuffer(txt, path string) *Buffer { b.savedText = txt b.Update() - - b.rules, b.filetype = GetRules(b) + b.UpdateRules() return b } +// UpdateRules updates the syntax rules and filetype for this buffer +// This is called when the colorscheme changes +func (b *Buffer) UpdateRules() { + b.rules, b.filetype = GetRules(b) +} + // Update fetches the string from the rope and updates the `text` and `lines` in the buffer func (b *Buffer) Update() { b.text = b.r.String() diff --git a/src/colorscheme.go b/src/colorscheme.go index 8d9f73fa..6b41d669 100644 --- a/src/colorscheme.go +++ b/src/colorscheme.go @@ -10,8 +10,6 @@ import ( "strings" ) -const defaultColorscheme = "default" - // Colorscheme is a map from string to style -- it represents a colorscheme type Colorscheme map[string]tcell.Style @@ -30,7 +28,7 @@ func LoadDefaultColorscheme() { TermMessage("Error finding your home directory\nCan't load runtime files") return } - LoadColorscheme(defaultColorscheme, dir+"/.micro/colorschemes") + LoadColorscheme(options["colorscheme"].(string), dir+"/.micro/colorschemes") } // LoadColorscheme loads the given colorscheme from a directory diff --git a/src/cursor.go b/src/cursor.go index 089ac1c7..8e78fe55 100644 --- a/src/cursor.go +++ b/src/cursor.go @@ -149,6 +149,7 @@ func (c *Cursor) Start() { // GetCharPosInLine gets the char position of a visual x y coordinate (this is necessary because tabs are 1 char but 4 visual spaces) func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int { + tabSize := options["tabsize"].(int) visualLine := strings.Replace(c.v.buf.lines[lineNum], "\t", "\t"+Spaces(tabSize-1), -1) if visualPos > Count(visualLine) { visualPos = Count(visualLine) @@ -163,6 +164,7 @@ func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int { // GetVisualX returns the x value of the cursor in visual spaces func (c *Cursor) GetVisualX() int { runes := []rune(c.v.buf.lines[c.y]) + tabSize := options["tabsize"].(int) return c.x + NumOccurences(string(runes[:c.x]), '\t')*(tabSize-1) } diff --git a/src/highlighter.go b/src/highlighter.go index 73f3dde1..86d9a0bc 100644 --- a/src/highlighter.go +++ b/src/highlighter.go @@ -256,9 +256,9 @@ func Match(v *View) SyntaxMatches { lineNum -= viewStart if lineNum >= 0 && lineNum < v.height { if lineNum >= len(matches) { - v.m.Error("Line " + strconv.Itoa(lineNum)) + messenger.Error("Line " + strconv.Itoa(lineNum)) } else if colNum >= len(matches[lineNum]) { - v.m.Error("Line " + strconv.Itoa(lineNum) + " Col " + strconv.Itoa(colNum) + " " + strconv.Itoa(len(matches[lineNum]))) + messenger.Error("Line " + strconv.Itoa(lineNum) + " Col " + strconv.Itoa(colNum) + " " + strconv.Itoa(len(matches[lineNum]))) } matches[lineNum][colNum] = rule.style } diff --git a/src/micro.go b/src/micro.go index 5e915bc0..6c5abe0a 100644 --- a/src/micro.go +++ b/src/micro.go @@ -10,7 +10,6 @@ import ( ) const ( - tabSize = 4 // This should be configurable synLinesUp = 75 // How many lines up to look to do syntax highlighting synLinesDown = 75 // How many lines down to look to do syntax highlighting ) @@ -18,6 +17,9 @@ const ( // The main screen var screen tcell.Screen +// Object to send messages and prompts to the user +var messenger *Messenger + // LoadInput loads the file input for the editor func LoadInput() (string, []byte, error) { // There are a number of ways micro should start given its input @@ -61,6 +63,8 @@ func main() { os.Exit(1) } + InitOptions() + // Should we enable true color? truecolor := os.Getenv("MICRO_TRUECOLOR") == "1" @@ -115,8 +119,8 @@ func main() { screen.SetStyle(defStyle) screen.EnableMouse() - messenger := new(Messenger) - view := NewView(NewBuffer(string(input), filename), messenger) + messenger = new(Messenger) + view := NewView(NewBuffer(string(input), filename)) for { // Display everything @@ -133,12 +137,15 @@ func main() { // Check if we should quit switch e := event.(type) { case *tcell.EventKey: - if e.Key() == tcell.KeyCtrlQ { + switch e.Key() { + case tcell.KeyCtrlQ: // Make sure not to quit if there are unsaved changes if view.CanClose("Quit anyway? ") { screen.Fini() os.Exit(0) } + case tcell.KeyCtrlE: + SetOption(view) } } diff --git a/src/option.go b/src/option.go new file mode 100644 index 00000000..606e12b5 --- /dev/null +++ b/src/option.go @@ -0,0 +1,45 @@ +package main + +import ( + "strconv" + "strings" +) + +// The options that the user can set +var options map[string]interface{} + +func InitOptions() { + options = make(map[string]interface{}) + options["tabsize"] = 4 + options["colorscheme"] = "default" +} + +func SetOption(view *View) { + choice, canceled := messenger.Prompt("Option: ") + if !canceled { + split := strings.Split(choice, "=") + if len(split) == 2 { + option := strings.TrimSpace(split[0]) + value := strings.TrimSpace(split[1]) + if _, exists := options[option]; exists { + if option == "tabsize" { + tsize, err := strconv.Atoi(value) + if err != nil { + messenger.Error("Invalid value for " + option) + return + } + options[option] = tsize + } + if option == "colorscheme" { + options[option] = value + LoadSyntaxFiles() + view.buf.UpdateRules() + } + } else { + messenger.Error("Option " + option + " does not exist") + } + } else { + messenger.Error("Invalid option, please use option = value") + } + } +} diff --git a/src/view.go b/src/view.go index 936814b6..824f7903 100644 --- a/src/view.go +++ b/src/view.go @@ -51,24 +51,19 @@ type View struct { // This is the range of lines that should have their syntax highlighting updated updateLines [2]int - - // The messenger so we can send messages to the user and get input from them - m *Messenger } // NewView returns a new fullscreen view -func NewView(buf *Buffer, m *Messenger) *View { - return NewViewWidthHeight(buf, m, 100, 100) +func NewView(buf *Buffer) *View { + return NewViewWidthHeight(buf, 100, 100) } // NewViewWidthHeight returns a new view with the specified width and height percentages // Note that w and h are percentages not actual values -func NewViewWidthHeight(buf *Buffer, m *Messenger, w, h int) *View { +func NewViewWidthHeight(buf *Buffer, w, h int) *View { v := new(View) v.buf = buf - // Messenger - v.m = m v.widthPercent = w v.heightPercent = h @@ -180,7 +175,7 @@ func (v *View) HalfPageDown() { // The message is what to print after saying "You have unsaved changes. " func (v *View) CanClose(msg string) bool { if v.buf.IsDirty() { - quit, canceled := v.m.Prompt("You have unsaved changes. " + msg) + quit, canceled := messenger.Prompt("You have unsaved changes. " + msg) if !canceled { if strings.ToLower(quit) == "yes" || strings.ToLower(quit) == "y" { return true @@ -196,7 +191,7 @@ func (v *View) CanClose(msg string) bool { func (v *View) Save() { // If this is an empty buffer, ask for a filename if v.buf.path == "" { - filename, canceled := v.m.Prompt("Filename: ") + filename, canceled := messenger.Prompt("Filename: ") if !canceled { v.buf.path = filename v.buf.name = filename @@ -206,7 +201,7 @@ func (v *View) Save() { } err := v.buf.Save() if err != nil { - v.m.Error(err.Error()) + messenger.Error(err.Error()) } } @@ -216,7 +211,7 @@ func (v *View) Copy() { if !clipboard.Unsupported { clipboard.WriteAll(v.cursor.GetSelection()) } else { - v.m.Error("Clipboard is not supported on your system") + messenger.Error("Clipboard is not supported on your system") } } } @@ -229,7 +224,7 @@ func (v *View) Cut() { v.cursor.DeleteSelection() v.cursor.ResetSelection() } else { - v.m.Error("Clipboard is not supported on your system") + messenger.Error("Clipboard is not supported on your system") } } } @@ -249,7 +244,7 @@ func (v *View) Paste() { v.cursor.Right() } } else { - v.m.Error("Clipboard is not supported on your system") + messenger.Error("Clipboard is not supported on your system") } } @@ -267,14 +262,14 @@ func (v *View) SelectAll() { // It makes sure that the current buffer can be closed first (unsaved changes) func (v *View) OpenFile() { if v.CanClose("Continue? ") { - filename, canceled := v.m.Prompt("File to open: ") + filename, canceled := messenger.Prompt("File to open: ") if canceled { return } file, err := ioutil.ReadFile(filename) if err != nil { - v.m.Error(err.Error()) + messenger.Error(err.Error()) return } v.buf = NewBuffer(string(file), filename) @@ -552,6 +547,7 @@ func (v *View) DisplayView() { if ch == '\t' { screen.SetContent(x+tabchars, lineN, ' ', nil, lineStyle) + tabSize := options["tabsize"].(int) for i := 0; i < tabSize-1; i++ { tabchars++ screen.SetContent(x+tabchars, lineN, ' ', nil, lineStyle) diff --git a/todolist.md b/todolist.md index 0d4c0dc3..0fea3400 100644 --- a/todolist.md +++ b/todolist.md @@ -4,6 +4,8 @@ - [ ] Optimization +- [ ] Documentation + - [ ] Search and replace - [ ] Better selection @@ -26,21 +28,21 @@ - [ ] Allow executing simple commands at the bottom of the editor (like vim or emacs) -- [ ] Options - - [ ] Add options such as tab size, use tabs or use spaces, etc... - ### Done -- [x] Readme - - [x] Line numbers -- [x] Tests +- [x] Simple tests - [x] Proper error handling - [x] Cleanup +- [x] Options + - [x] Colorscheme + - [x] tab size + - [ ] tabs or spaces + - [x] Syntax highlighting - [x] Use nano-like syntax files (https://github.com/scopatz/nanorc)