]> git.lizzy.rs Git - micro.git/blob - cmd/micro/micro.go
Resizing work
[micro.git] / cmd / micro / micro.go
1 package main
2
3 import (
4         "flag"
5         "fmt"
6         "io/ioutil"
7         "os"
8         "strings"
9
10         "github.com/go-errors/errors"
11         isatty "github.com/mattn/go-isatty"
12         "github.com/zyedidia/micro/cmd/micro/action"
13         "github.com/zyedidia/micro/cmd/micro/buffer"
14         "github.com/zyedidia/micro/cmd/micro/config"
15         "github.com/zyedidia/micro/cmd/micro/screen"
16         "github.com/zyedidia/micro/cmd/micro/util"
17         "github.com/zyedidia/tcell"
18 )
19
20 var (
21         // These variables should be set by the linker when compiling
22
23         // Version is the version number or commit hash
24         Version = "0.0.0-unknown"
25         // CommitHash is the commit this version was built on
26         CommitHash = "Unknown"
27         // CompileDate is the date this binary was compiled on
28         CompileDate = "Unknown"
29         // Debug logging
30         Debug = "ON"
31
32         // Event channel
33         events   chan tcell.Event
34         autosave chan bool
35
36         // Command line flags
37         flagVersion   = flag.Bool("version", false, "Show the version number and information")
38         flagStartPos  = flag.String("startpos", "", "LINE,COL to start the cursor at when opening a buffer.")
39         flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
40         flagOptions   = flag.Bool("options", false, "Show all option help")
41 )
42
43 func InitFlags() {
44         flag.Usage = func() {
45                 fmt.Println("Usage: micro [OPTIONS] [FILE]...")
46                 fmt.Println("-config-dir dir")
47                 fmt.Println("    \tSpecify a custom location for the configuration directory")
48                 fmt.Println("-startpos LINE,COL")
49                 fmt.Println("+LINE:COL")
50                 fmt.Println("    \tSpecify a line and column to start the cursor at when opening a buffer")
51                 fmt.Println("    \tThis can also be done by opening file:LINE:COL")
52                 fmt.Println("-options")
53                 fmt.Println("    \tShow all option help")
54                 fmt.Println("-version")
55                 fmt.Println("    \tShow the version number and information")
56
57                 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")
58                 fmt.Println("-option value")
59                 fmt.Println("    \tSet `option` to `value` for this session")
60                 fmt.Println("    \tFor example: `micro -syntax off file.c`")
61                 fmt.Println("\nUse `micro -options` to see the full list of configuration options")
62         }
63
64         optionFlags := make(map[string]*string)
65
66         for k, v := range config.DefaultGlobalSettings() {
67                 optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'", k, v))
68         }
69
70         flag.Parse()
71
72         if *flagVersion {
73                 // If -version was passed
74                 fmt.Println("Version:", Version)
75                 fmt.Println("Commit hash:", CommitHash)
76                 fmt.Println("Compiled on", CompileDate)
77                 os.Exit(0)
78         }
79
80         if *flagOptions {
81                 // If -options was passed
82                 for k, v := range config.DefaultGlobalSettings() {
83                         fmt.Printf("-%s value\n", k)
84                         fmt.Printf("    \tDefault value: '%v'\n", v)
85                 }
86                 os.Exit(0)
87         }
88 }
89
90 // LoadInput determines which files should be loaded into buffers
91 // based on the input stored in flag.Args()
92 func LoadInput() []*buffer.Buffer {
93         // There are a number of ways micro should start given its input
94
95         // 1. If it is given a files in flag.Args(), it should open those
96
97         // 2. If there is no input file and the input is not a terminal, that means
98         // something is being piped in and the stdin should be opened in an
99         // empty buffer
100
101         // 3. If there is no input file and the input is a terminal, an empty buffer
102         // should be opened
103
104         var filename string
105         var input []byte
106         var err error
107         args := flag.Args()
108         buffers := make([]*buffer.Buffer, 0, len(args))
109
110         if len(args) > 0 {
111                 // Option 1
112                 // We go through each file and load it
113                 for i := 0; i < len(args); i++ {
114                         if strings.HasPrefix(args[i], "+") {
115                                 if strings.Contains(args[i], ":") {
116                                         split := strings.Split(args[i], ":")
117                                         *flagStartPos = split[0][1:] + "," + split[1]
118                                 } else {
119                                         *flagStartPos = args[i][1:] + ",0"
120                                 }
121                                 continue
122                         }
123
124                         buf, err := buffer.NewBufferFromFile(args[i], buffer.BTDefault)
125                         if err != nil {
126                                 util.TermMessage(err)
127                                 continue
128                         }
129                         // If the file didn't exist, input will be empty, and we'll open an empty buffer
130                         buffers = append(buffers, buf)
131                 }
132         } else if !isatty.IsTerminal(os.Stdin.Fd()) {
133                 // Option 2
134                 // The input is not a terminal, so something is being piped in
135                 // and we should read from stdin
136                 input, err = ioutil.ReadAll(os.Stdin)
137                 if err != nil {
138                         util.TermMessage("Error reading from stdin: ", err)
139                         input = []byte{}
140                 }
141                 buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, buffer.BTDefault))
142         } else {
143                 // Option 3, just open an empty buffer
144                 buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, buffer.BTDefault))
145         }
146
147         return buffers
148 }
149
150 func main() {
151         var err error
152
153         InitLog()
154         InitFlags()
155         err = config.InitConfigDir(*flagConfigDir)
156         if err != nil {
157                 util.TermMessage(err)
158         }
159         config.InitRuntimeFiles()
160         err = config.ReadSettings()
161         if err != nil {
162                 util.TermMessage(err)
163         }
164         config.InitGlobalSettings()
165         action.InitBindings()
166         action.InitCommands()
167
168         err = config.InitColorscheme()
169         if err != nil {
170                 util.TermMessage(err)
171         }
172
173         screen.Init()
174
175         // If we have an error, we can exit cleanly and not completely
176         // mess up the terminal being worked in
177         // In other words we need to shut down tcell before the program crashes
178         defer func() {
179                 if err := recover(); err != nil {
180                         screen.Screen.Fini()
181                         fmt.Println("Micro encountered an error:", err)
182                         // Print the stack trace too
183                         fmt.Print(errors.Wrap(err, 2).ErrorStack())
184                         os.Exit(1)
185                 }
186         }()
187
188         b := LoadInput()[0]
189         width, height := screen.Screen.Size()
190
191         action.MainTab = action.NewTabPane(width, height-1, b)
192
193         action.InitGlobals()
194
195         // Here is the event loop which runs in a separate thread
196         // go func() {
197         //      events = make(chan tcell.Event)
198         //      for {
199         //              screen.Lock()
200         //              events <- screen.Screen.PollEvent()
201         //              screen.Unlock()
202         //      }
203         // }()
204
205         for {
206                 // Display everything
207                 screen.Screen.Fill(' ', config.DefStyle)
208                 screen.Screen.HideCursor()
209                 for _, ep := range action.MainTab.Panes {
210                         ep.Display()
211                 }
212                 action.MainTab.Display()
213                 action.InfoBar.Display()
214                 screen.Screen.Show()
215
216                 var event tcell.Event
217
218                 // Check for new events
219                 screen.Lock()
220                 event = screen.Screen.PollEvent()
221                 screen.Unlock()
222                 // select {
223                 // case event = <-events:
224                 // }
225
226                 if event != nil {
227                         if action.InfoBar.HasPrompt {
228                                 action.InfoBar.HandleEvent(event)
229                         } else {
230                                 action.MainTab.HandleEvent(event)
231                         }
232                 }
233         }
234 }