12 "github.com/go-errors/errors"
13 isatty "github.com/mattn/go-isatty"
14 "github.com/zyedidia/micro/internal/action"
15 "github.com/zyedidia/micro/internal/buffer"
16 "github.com/zyedidia/micro/internal/config"
17 "github.com/zyedidia/micro/internal/screen"
18 "github.com/zyedidia/micro/internal/shell"
19 "github.com/zyedidia/micro/internal/util"
20 "github.com/zyedidia/tcell"
25 events chan tcell.Event
29 flagVersion = flag.Bool("version", false, "Show the version number and information")
30 flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
31 flagOptions = flag.Bool("options", false, "Show all option help")
32 flagDebug = flag.Bool("debug", false, "Enable debug mode (prints debug info to ./log.txt)")
33 flagPlugin = flag.String("plugin", "", "Plugin command")
34 flagClean = flag.Bool("clean", false, "Clean configuration directory")
35 optionFlags map[string]*string
40 fmt.Println("Usage: micro [OPTIONS] [FILE]...")
42 fmt.Println(" \tCleans the configuration directory")
43 fmt.Println("-config-dir dir")
44 fmt.Println(" \tSpecify a custom location for the configuration directory")
45 fmt.Println("[FILE]:LINE:COL")
46 fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
47 fmt.Println("-options")
48 fmt.Println(" \tShow all option help")
50 fmt.Println(" \tEnable debug mode (enables logging to ./log.txt)")
51 fmt.Println("-version")
52 fmt.Println(" \tShow the version number and information")
54 fmt.Print("\nMicro's plugin's can be managed at the command line with the following commands.\n")
55 fmt.Println("-plugin install [PLUGIN]...")
56 fmt.Println(" \tInstall plugin(s)")
57 fmt.Println("-plugin remove [PLUGIN]...")
58 fmt.Println(" \tRemove plugin(s)")
59 fmt.Println("-plugin update [PLUGIN]...")
60 fmt.Println(" \tUpdate plugin(s) (if no argument is given, updates all plugins)")
61 fmt.Println("-plugin search [PLUGIN]...")
62 fmt.Println(" \tSearch for a plugin")
63 fmt.Println("-plugin list")
64 fmt.Println(" \tList installed plugins")
65 fmt.Println("-plugin available")
66 fmt.Println(" \tList available plugins")
68 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")
69 fmt.Println("-option value")
70 fmt.Println(" \tSet `option` to `value` for this session")
71 fmt.Println(" \tFor example: `micro -syntax off file.c`")
72 fmt.Println("\nUse `micro -options` to see the full list of configuration options")
75 optionFlags = make(map[string]*string)
77 for k, v := range config.DefaultAllSettings() {
78 optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'.", k, v))
84 // If -version was passed
85 fmt.Println("Version:", util.Version)
86 fmt.Println("Commit hash:", util.CommitHash)
87 fmt.Println("Compiled on", util.CompileDate)
92 // If -options was passed
94 m := config.DefaultAllSettings()
96 keys = append(keys, k)
99 for _, k := range keys {
101 fmt.Printf("-%s value\n", k)
102 fmt.Printf(" \tDefault value: '%v'\n", v)
107 if util.Debug == "OFF" && *flagDebug {
112 // DoPluginFlags parses and executes any flags that require LoadAllPlugins (-plugin and -clean)
113 func DoPluginFlags() {
114 if *flagClean || *flagPlugin != "" {
115 config.LoadAllPlugins()
117 if *flagPlugin != "" {
120 config.PluginCommand(os.Stdout, *flagPlugin, args)
121 } else if *flagClean {
129 // LoadInput determines which files should be loaded into buffers
130 // based on the input stored in flag.Args()
131 func LoadInput() []*buffer.Buffer {
132 // There are a number of ways micro should start given its input
134 // 1. If it is given a files in flag.Args(), it should open those
136 // 2. If there is no input file and the input is not a terminal, that means
137 // something is being piped in and the stdin should be opened in an
140 // 3. If there is no input file and the input is a terminal, an empty buffer
147 buffers := make([]*buffer.Buffer, 0, len(args))
151 // We go through each file and load it
152 for i := 0; i < len(args); i++ {
153 buf, err := buffer.NewBufferFromFile(args[i], buffer.BTDefault)
155 screen.TermMessage(err)
158 // If the file didn't exist, input will be empty, and we'll open an empty buffer
159 buffers = append(buffers, buf)
161 } else if !isatty.IsTerminal(os.Stdin.Fd()) {
163 // The input is not a terminal, so something is being piped in
164 // and we should read from stdin
165 input, err = ioutil.ReadAll(os.Stdin)
167 screen.TermMessage("Error reading from stdin: ", err)
170 buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, buffer.BTDefault))
172 // Option 3, just open an empty buffer
173 buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, buffer.BTDefault))
182 // runtime.SetCPUProfileRate(400)
183 // f, _ := os.Create("micro.prof")
184 // pprof.StartCPUProfile(f)
185 // defer pprof.StopCPUProfile()
193 err = config.InitConfigDir(*flagConfigDir)
195 screen.TermMessage(err)
198 config.InitRuntimeFiles()
199 err = config.ReadSettings()
201 screen.TermMessage(err)
203 config.InitGlobalSettings()
206 for k, v := range optionFlags {
208 nativeValue, err := config.GetNativeValue(k, config.DefaultAllSettings()[k], *v)
210 screen.TermMessage(err)
213 config.GlobalSettings[k] = nativeValue
221 // If we have an error, we can exit cleanly and not completely
222 // mess up the terminal being worked in
223 // In other words we need to shut down tcell before the program crashes
225 if err := recover(); err != nil {
227 fmt.Println("Micro encountered an error:", err)
228 // backup all open buffers
229 for _, b := range buffer.OpenBuffers {
232 // Print the stack trace too
233 fmt.Print(errors.Wrap(err, 2).ErrorStack())
238 err = config.LoadAllPlugins()
240 screen.TermMessage(err)
243 action.InitBindings()
244 action.InitCommands()
246 err = config.InitColorscheme()
248 screen.TermMessage(err)
254 // No buffers to open
262 err = config.RunPluginFn("init")
264 screen.TermMessage(err)
267 events = make(chan tcell.Event)
269 // Here is the event loop which runs in a separate thread
273 e := screen.Screen.PollEvent()
281 // clear the drawchan so we don't redraw excessively
282 // if someone requested a redraw before we started displaying
283 for len(screen.DrawChan) > 0 {
287 var event tcell.Event
289 // wait for initial resize event
291 case event = <-events:
292 action.Tabs.HandleEvent(event)
293 case <-time.After(10 * time.Millisecond):
294 // time out after 10ms
298 // Display everything
299 screen.Screen.Fill(' ', config.DefStyle)
300 screen.Screen.HideCursor()
301 action.Tabs.Display()
302 for _, ep := range action.MainTab().Panes {
305 action.MainTab().Display()
306 action.InfoBar.Display()
311 // Check for new events
313 case f := <-shell.Jobs:
314 // If a new job has finished while running in the background we should execute the callback
315 f.Function(f.Output, f.Args...)
316 case <-config.Autosave:
317 for _, b := range buffer.OpenBuffers {
320 case <-shell.CloseTerms:
321 case event = <-events:
322 case <-screen.DrawChan:
325 if action.InfoBar.HasPrompt {
326 action.InfoBar.HandleEvent(event)
328 action.Tabs.HandleEvent(event)