]> git.lizzy.rs Git - micro.git/blob - src/micro.go
Update todolist
[micro.git] / src / micro.go
1 package main
2
3 import (
4         "fmt"
5         "github.com/gdamore/tcell"
6         "github.com/go-errors/errors"
7         "github.com/mattn/go-isatty"
8         "io/ioutil"
9         "os"
10 )
11
12 const (
13         tabSize      = 4  // This should be configurable
14         synLinesUp   = 75 // How many lines up to look to do syntax highlighting
15         synLinesDown = 75 // How many lines down to look to do syntax highlighting
16 )
17
18 // The main screen
19 var screen tcell.Screen
20
21 // LoadInput loads the file input for the editor
22 func LoadInput() (string, []byte, error) {
23         // There are a number of ways micro should start given its input
24         // 1. If it is given a file in os.Args, it should open that
25
26         // 2. If there is no input file and the input is not a terminal, that means
27         // something is being piped in and the stdin should be opened in an
28         // empty buffer
29
30         // 3. If there is no input file and the input is a terminal, an empty buffer
31         // should be opened
32
33         // These are empty by default so if we get to option 3, we can just returns the
34         // default values
35         var filename string
36         var input []byte
37         var err error
38
39         if len(os.Args) > 1 {
40                 // Option 1
41                 filename = os.Args[1]
42                 // Check that the file exists
43                 if _, err := os.Stat(filename); err == nil {
44                         input, err = ioutil.ReadFile(filename)
45                 }
46         } else if !isatty.IsTerminal(os.Stdin.Fd()) {
47                 // Option 2
48                 // The input is not a terminal, so something is being piped in
49                 // and we should read from stdin
50                 input, err = ioutil.ReadAll(os.Stdin)
51         }
52
53         // Option 3, or just return whatever we got
54         return filename, input, err
55 }
56
57 func main() {
58         filename, input, err := LoadInput()
59         if err != nil {
60                 fmt.Println(err)
61                 os.Exit(1)
62         }
63
64         // Should we enable true color?
65         truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"
66
67         // In order to enable true color, we have to set the TERM to `xterm-truecolor` when
68         // initializing tcell, but after that, we can set the TERM back to whatever it was
69         oldTerm := os.Getenv("TERM")
70         if truecolor {
71                 os.Setenv("TERM", "xterm-truecolor")
72         }
73
74         // Load the syntax files, including the colorscheme
75         LoadSyntaxFiles()
76
77         // Initilize tcell
78         screen, err = tcell.NewScreen()
79         if err != nil {
80                 fmt.Println(err)
81                 os.Exit(1)
82         }
83         if err = screen.Init(); err != nil {
84                 fmt.Println(err)
85                 os.Exit(1)
86         }
87
88         // Now we can put the TERM back to what it was before
89         if truecolor {
90                 os.Setenv("TERM", oldTerm)
91         }
92
93         // This is just so if we have an error, we can exit cleanly and not completely
94         // mess up the terminal being worked in
95         defer func() {
96                 if err := recover(); err != nil {
97                         screen.Fini()
98                         fmt.Println("Micro encountered an error:", err)
99                         // Print the stack trace too
100                         fmt.Print(errors.Wrap(err, 2).ErrorStack())
101                         os.Exit(1)
102                 }
103         }()
104
105         // Default style
106         defStyle := tcell.StyleDefault.
107                 Foreground(tcell.ColorDefault).
108                 Background(tcell.ColorDefault)
109
110         // There may be another default style defined in the colorscheme
111         if style, ok := colorscheme["default"]; ok {
112                 defStyle = style
113         }
114
115         screen.SetStyle(defStyle)
116         screen.EnableMouse()
117
118         messenger := new(Messenger)
119         view := NewView(NewBuffer(string(input), filename), messenger)
120
121         for {
122                 // Display everything
123                 screen.Clear()
124
125                 view.Display()
126                 messenger.Display()
127
128                 screen.Show()
129
130                 // Wait for the user's action
131                 event := screen.PollEvent()
132
133                 // Check if we should quit
134                 switch e := event.(type) {
135                 case *tcell.EventKey:
136                         if e.Key() == tcell.KeyCtrlQ {
137                                 // Make sure not to quit if there are unsaved changes
138                                 if view.CanClose("Quit anyway? ") {
139                                         screen.Fini()
140                                         os.Exit(0)
141                                 }
142                         }
143                 }
144
145                 // Send it to the view
146                 view.HandleEvent(event)
147         }
148 }