13 "github.com/go-errors/errors"
14 isatty "github.com/mattn/go-isatty"
15 lua "github.com/yuin/gopher-lua"
16 "github.com/zyedidia/micro/v2/internal/action"
17 "github.com/zyedidia/micro/v2/internal/buffer"
18 "github.com/zyedidia/micro/v2/internal/config"
19 "github.com/zyedidia/micro/v2/internal/screen"
20 "github.com/zyedidia/micro/v2/internal/shell"
21 "github.com/zyedidia/micro/v2/internal/util"
22 "github.com/zyedidia/tcell"
27 events chan tcell.Event
31 flagVersion = flag.Bool("version", false, "Show the version number and information")
32 flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
33 flagOptions = flag.Bool("options", false, "Show all option help")
34 flagDebug = flag.Bool("debug", false, "Enable debug mode (prints debug info to ./log.txt)")
35 flagPlugin = flag.String("plugin", "", "Plugin command")
36 flagClean = flag.Bool("clean", false, "Clean configuration directory")
37 optionFlags map[string]*string
42 fmt.Println("Usage: micro [OPTIONS] [FILE]...")
44 fmt.Println(" \tCleans the configuration directory")
45 fmt.Println("-config-dir dir")
46 fmt.Println(" \tSpecify a custom location for the configuration directory")
47 fmt.Println("[FILE]:LINE:COL")
48 fmt.Println("+LINE:COL")
49 fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
50 fmt.Println("-options")
51 fmt.Println(" \tShow all option help")
53 fmt.Println(" \tEnable debug mode (enables logging to ./log.txt)")
54 fmt.Println("-version")
55 fmt.Println(" \tShow the version number and information")
57 fmt.Print("\nMicro's plugin's can be managed at the command line with the following commands.\n")
58 fmt.Println("-plugin install [PLUGIN]...")
59 fmt.Println(" \tInstall plugin(s)")
60 fmt.Println("-plugin remove [PLUGIN]...")
61 fmt.Println(" \tRemove plugin(s)")
62 fmt.Println("-plugin update [PLUGIN]...")
63 fmt.Println(" \tUpdate plugin(s) (if no argument is given, updates all plugins)")
64 fmt.Println("-plugin search [PLUGIN]...")
65 fmt.Println(" \tSearch for a plugin")
66 fmt.Println("-plugin list")
67 fmt.Println(" \tList installed plugins")
68 fmt.Println("-plugin available")
69 fmt.Println(" \tList available plugins")
71 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")
72 fmt.Println("-option value")
73 fmt.Println(" \tSet `option` to `value` for this session")
74 fmt.Println(" \tFor example: `micro -syntax off file.c`")
75 fmt.Println("\nUse `micro -options` to see the full list of configuration options")
78 optionFlags = make(map[string]*string)
80 for k, v := range config.DefaultAllSettings() {
81 optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'.", k, v))
87 // If -version was passed
88 fmt.Println("Version:", util.Version)
89 fmt.Println("Commit hash:", util.CommitHash)
90 fmt.Println("Compiled on", util.CompileDate)
95 // If -options was passed
97 m := config.DefaultAllSettings()
99 keys = append(keys, k)
102 for _, k := range keys {
104 fmt.Printf("-%s value\n", k)
105 fmt.Printf(" \tDefault value: '%v'\n", v)
110 if util.Debug == "OFF" && *flagDebug {
115 // DoPluginFlags parses and executes any flags that require LoadAllPlugins (-plugin and -clean)
116 func DoPluginFlags() {
117 if *flagClean || *flagPlugin != "" {
118 config.LoadAllPlugins()
120 if *flagPlugin != "" {
123 config.PluginCommand(os.Stdout, *flagPlugin, args)
124 } else if *flagClean {
132 // LoadInput determines which files should be loaded into buffers
133 // based on the input stored in flag.Args()
134 func LoadInput() []*buffer.Buffer {
135 // There are a number of ways micro should start given its input
137 // 1. If it is given a files in flag.Args(), it should open those
139 // 2. If there is no input file and the input is not a terminal, that means
140 // something is being piped in and the stdin should be opened in an
143 // 3. If there is no input file and the input is a terminal, an empty buffer
150 buffers := make([]*buffer.Buffer, 0, len(args))
152 btype := buffer.BTDefault
153 if !isatty.IsTerminal(os.Stdout.Fd()) {
154 btype = buffer.BTStdout
157 files := make([]string, 0, len(args))
159 flagr := regexp.MustCompile(`^\+\d+(:\d+)?$`)
160 for _, a := range args {
161 if flagr.MatchString(a) {
164 if flagStartPos != "" {
165 files = append(files, a+":"+flagStartPos)
168 files = append(files, a)
175 // We go through each file and load it
176 for i := 0; i < len(files); i++ {
177 buf, err := buffer.NewBufferFromFile(files[i], btype)
179 screen.TermMessage(err)
182 // If the file didn't exist, input will be empty, and we'll open an empty buffer
183 buffers = append(buffers, buf)
185 } else if !isatty.IsTerminal(os.Stdin.Fd()) {
187 // The input is not a terminal, so something is being piped in
188 // and we should read from stdin
189 input, err = ioutil.ReadAll(os.Stdin)
191 screen.TermMessage("Error reading from stdin: ", err)
194 buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, btype))
196 // Option 3, just open an empty buffer
197 buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, btype))
205 if util.Stdout.Len() > 0 {
206 fmt.Fprint(os.Stdout, util.Stdout.String())
211 // runtime.SetCPUProfileRate(400)
212 // f, _ := os.Create("micro.prof")
213 // pprof.StartCPUProfile(f)
214 // defer pprof.StopCPUProfile()
222 err = config.InitConfigDir(*flagConfigDir)
224 screen.TermMessage(err)
227 config.InitRuntimeFiles()
228 err = config.ReadSettings()
230 screen.TermMessage(err)
232 config.InitGlobalSettings()
235 for k, v := range optionFlags {
237 nativeValue, err := config.GetNativeValue(k, config.DefaultAllSettings()[k], *v)
239 screen.TermMessage(err)
242 config.GlobalSettings[k] = nativeValue
251 if err := recover(); err != nil {
253 fmt.Println("Micro encountered an error:", err)
254 // backup all open buffers
255 for _, b := range buffer.OpenBuffers {
258 // Print the stack trace too
259 fmt.Print(errors.Wrap(err, 2).ErrorStack())
264 err = config.LoadAllPlugins()
266 screen.TermMessage(err)
269 action.InitBindings()
270 action.InitCommands()
272 err = config.InitColorscheme()
274 screen.TermMessage(err)
280 // No buffers to open
288 err = config.RunPluginFn("init")
290 screen.TermMessage(err)
293 events = make(chan tcell.Event)
295 // Here is the event loop which runs in a separate thread
299 e := screen.Screen.PollEvent()
307 // clear the drawchan so we don't redraw excessively
308 // if someone requested a redraw before we started displaying
309 for len(screen.DrawChan()) > 0 {
313 // wait for initial resize event
315 case event := <-events:
316 action.Tabs.HandleEvent(event)
317 case <-time.After(10 * time.Millisecond):
318 // time out after 10ms
321 // Since this loop is very slow (waits for user input every time) it's
322 // okay to be inefficient and run it via a function every time
323 // We do this so we can recover from panics without crashing the editor
329 // DoEvent runs the main action loop of the editor
331 var event tcell.Event
333 // recover from errors without crashing the editor
335 if err := recover(); err != nil {
336 if e, ok := err.(*lua.ApiError); ok {
337 screen.TermMessage("Lua API error:", e)
339 screen.TermMessage("Micro encountered an error:", errors.Wrap(err, 2).ErrorStack(), "\nIf you can reproduce this error, please report it at https://github.com/zyedidia/micro/issues")
343 // Display everything
344 screen.Screen.Fill(' ', config.DefStyle)
345 screen.Screen.HideCursor()
346 action.Tabs.Display()
347 for _, ep := range action.MainTab().Panes {
350 action.MainTab().Display()
351 action.InfoBar.Display()
354 // Check for new events
356 case f := <-shell.Jobs:
357 // If a new job has finished while running in the background we should execute the callback
358 f.Function(f.Output, f.Args)
359 case <-config.Autosave:
360 for _, b := range buffer.OpenBuffers {
363 case <-shell.CloseTerms:
364 case event = <-events:
365 case <-screen.DrawChan():
366 for len(screen.DrawChan()) > 0 {
371 if action.InfoBar.HasPrompt {
372 action.InfoBar.HandleEvent(event)
374 action.Tabs.HandleEvent(event)