]> git.lizzy.rs Git - micro.git/blob - cmd/micro/micro.go
Start refactor
[micro.git] / cmd / micro / micro.go
1 package main
2
3 import (
4         "flag"
5         "fmt"
6         "os"
7         "time"
8
9         "github.com/go-errors/errors"
10         homedir "github.com/mitchellh/go-homedir"
11         "github.com/zyedidia/micro/cmd/micro/terminfo"
12         "github.com/zyedidia/tcell"
13 )
14
15 const (
16         doubleClickThreshold = 400 // How many milliseconds to wait before a second click is not a double click
17         undoThreshold        = 500 // If two events are less than n milliseconds apart, undo both of them
18         autosaveTime         = 8   // Number of seconds to wait before autosaving
19 )
20
21 var (
22         // The main screen
23         screen tcell.Screen
24
25         // Where the user's configuration is
26         // This should be $XDG_CONFIG_HOME/micro
27         // If $XDG_CONFIG_HOME is not set, it is ~/.config/micro
28         configDir string
29
30         // Version is the version number or commit hash
31         // These variables should be set by the linker when compiling
32         Version = "0.0.0-unknown"
33         // CommitHash is the commit this version was built on
34         CommitHash = "Unknown"
35         // CompileDate is the date this binary was compiled on
36         CompileDate = "Unknown"
37         // Debug logging
38         Debug = "ON"
39
40         // Event channel
41         events   chan tcell.Event
42         autosave chan bool
43
44         // How many redraws have happened
45         numRedraw uint
46
47         // Command line flags
48         flagVersion   = flag.Bool("version", false, "Show the version number and information")
49         flagStartPos  = flag.String("startpos", "", "LINE,COL to start the cursor at when opening a buffer.")
50         flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
51         flagOptions   = flag.Bool("options", false, "Show all option help")
52 )
53
54 // InitConfigDir finds the configuration directory for micro according to the XDG spec.
55 // If no directory is found, it creates one.
56 func InitConfigDir() {
57         xdgHome := os.Getenv("XDG_CONFIG_HOME")
58         if xdgHome == "" {
59                 // The user has not set $XDG_CONFIG_HOME so we should act like it was set to ~/.config
60                 home, err := homedir.Dir()
61                 if err != nil {
62                         TermMessage("Error finding your home directory\nCan't load config files")
63                         return
64                 }
65                 xdgHome = home + "/.config"
66         }
67         configDir = xdgHome + "/micro"
68
69         if len(*flagConfigDir) > 0 {
70                 if _, err := os.Stat(*flagConfigDir); os.IsNotExist(err) {
71                         TermMessage("Error: " + *flagConfigDir + " does not exist. Defaulting to " + configDir + ".")
72                 } else {
73                         configDir = *flagConfigDir
74                         return
75                 }
76         }
77
78         if _, err := os.Stat(xdgHome); os.IsNotExist(err) {
79                 // If the xdgHome doesn't exist we should create it
80                 err = os.Mkdir(xdgHome, os.ModePerm)
81                 if err != nil {
82                         TermMessage("Error creating XDG_CONFIG_HOME directory: " + err.Error())
83                 }
84         }
85
86         if _, err := os.Stat(configDir); os.IsNotExist(err) {
87                 // If the micro specific config directory doesn't exist we should create that too
88                 err = os.Mkdir(configDir, os.ModePerm)
89                 if err != nil {
90                         TermMessage("Error creating configuration directory: " + err.Error())
91                 }
92         }
93 }
94
95 // InitScreen creates and initializes the tcell screen
96 func InitScreen() {
97         // Should we enable true color?
98         truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"
99
100         tcelldb := os.Getenv("TCELLDB")
101         os.Setenv("TCELLDB", configDir+"/.tcelldb")
102
103         // In order to enable true color, we have to set the TERM to `xterm-truecolor` when
104         // initializing tcell, but after that, we can set the TERM back to whatever it was
105         oldTerm := os.Getenv("TERM")
106         if truecolor {
107                 os.Setenv("TERM", "xterm-truecolor")
108         }
109
110         // Initilize tcell
111         var err error
112         screen, err = tcell.NewScreen()
113         if err != nil {
114                 if err == tcell.ErrTermNotFound {
115                         err = terminfo.WriteDB(configDir + "/.tcelldb")
116                         if err != nil {
117                                 fmt.Println(err)
118                                 fmt.Println("Fatal: Micro could not create tcelldb")
119                                 os.Exit(1)
120                         }
121                         screen, err = tcell.NewScreen()
122                         if err != nil {
123                                 fmt.Println(err)
124                                 fmt.Println("Fatal: Micro could not initialize a screen.")
125                                 os.Exit(1)
126                         }
127                 } else {
128                         fmt.Println(err)
129                         fmt.Println("Fatal: Micro could not initialize a screen.")
130                         os.Exit(1)
131                 }
132         }
133         if err = screen.Init(); err != nil {
134                 fmt.Println(err)
135                 os.Exit(1)
136         }
137
138         // Now we can put the TERM back to what it was before
139         if truecolor {
140                 os.Setenv("TERM", oldTerm)
141         }
142
143         if GetGlobalOption("mouse").(bool) {
144                 screen.EnableMouse()
145         }
146
147         os.Setenv("TCELLDB", tcelldb)
148
149         // screen.SetStyle(defStyle)
150 }
151
152 func InitFlags() {
153         flag.Usage = func() {
154                 fmt.Println("Usage: micro [OPTIONS] [FILE]...")
155                 fmt.Println("-config-dir dir")
156                 fmt.Println("    \tSpecify a custom location for the configuration directory")
157                 fmt.Println("-startpos LINE,COL")
158                 fmt.Println("+LINE:COL")
159                 fmt.Println("    \tSpecify a line and column to start the cursor at when opening a buffer")
160                 fmt.Println("    \tThis can also be done by opening file:LINE:COL")
161                 fmt.Println("-options")
162                 fmt.Println("    \tShow all option help")
163                 fmt.Println("-version")
164                 fmt.Println("    \tShow the version number and information")
165
166                 fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the settings.json\nfile (see 'help options').\n\n")
167                 fmt.Println("-option value")
168                 fmt.Println("    \tSet `option` to `value` for this session")
169                 fmt.Println("    \tFor example: `micro -syntax off file.c`")
170                 fmt.Println("\nUse `micro -options` to see the full list of configuration options")
171         }
172
173         optionFlags := make(map[string]*string)
174
175         for k, v := range DefaultGlobalSettings() {
176                 optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'", k, v))
177         }
178
179         flag.Parse()
180
181         if *flagVersion {
182                 // If -version was passed
183                 fmt.Println("Version:", Version)
184                 fmt.Println("Commit hash:", CommitHash)
185                 fmt.Println("Compiled on", CompileDate)
186                 os.Exit(0)
187         }
188
189         if *flagOptions {
190                 // If -options was passed
191                 for k, v := range DefaultGlobalSettings() {
192                         fmt.Printf("-%s value\n", k)
193                         fmt.Printf("    \tDefault value: '%v'\n", v)
194                 }
195                 os.Exit(0)
196         }
197 }
198
199 func main() {
200         var err error
201
202         InitLog()
203         InitFlags()
204         InitConfigDir()
205         InitRuntimeFiles()
206         err = ReadSettings()
207         if err != nil {
208                 TermMessage(err)
209         }
210         InitGlobalSettings()
211         err = InitColorscheme()
212         if err != nil {
213                 TermMessage(err)
214         }
215
216         InitScreen()
217
218         // If we have an error, we can exit cleanly and not completely
219         // mess up the terminal being worked in
220         // In other words we need to shut down tcell before the program crashes
221         defer func() {
222                 if err := recover(); err != nil {
223                         screen.Fini()
224                         fmt.Println("Micro encountered an error:", err)
225                         // Print the stack trace too
226                         fmt.Print(errors.Wrap(err, 2).ErrorStack())
227                         os.Exit(1)
228                 }
229         }()
230
231         b, err := NewBufferFromFile(os.Args[1])
232
233         if err != nil {
234                 TermMessage(err)
235         }
236
237         width, height := screen.Size()
238
239         w := NewWindow(0, 0, width/2, height/2, b)
240
241         for i := 0; i < 5; i++ {
242                 screen.Clear()
243                 w.DisplayBuffer()
244                 w.DisplayStatusLine()
245                 screen.Show()
246                 time.Sleep(200 * time.Millisecond)
247                 w.StartLine++
248         }
249
250         // time.Sleep(2 * time.Second)
251
252         screen.Fini()
253 }