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()
"strings"
)
-const defaultColorscheme = "default"
-
// Colorscheme is a map from string to style -- it represents a colorscheme
type Colorscheme map[string]tcell.Style
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
// 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)
// 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)
}
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
}
)
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
)
// 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
os.Exit(1)
}
+ InitOptions()
+
// Should we enable true color?
truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"
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
// 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)
}
}
--- /dev/null
+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")
+ }
+ }
+}
// 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
// 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
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
}
err := v.buf.Save()
if err != nil {
- v.m.Error(err.Error())
+ messenger.Error(err.Error())
}
}
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")
}
}
}
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")
}
}
}
v.cursor.Right()
}
} else {
- v.m.Error("Clipboard is not supported on your system")
+ messenger.Error("Clipboard is not supported on your system")
}
}
// 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)
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)
- [ ] Optimization
+- [ ] Documentation
+
- [ ] Search and replace
- [ ] Better selection
- [ ] 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)