17 "github.com/go-errors/errors"
18 isatty "github.com/mattn/go-isatty"
19 lua "github.com/yuin/gopher-lua"
20 "github.com/zyedidia/micro/v2/internal/action"
21 "github.com/zyedidia/micro/v2/internal/buffer"
22 "github.com/zyedidia/micro/v2/internal/clipboard"
23 "github.com/zyedidia/micro/v2/internal/config"
24 ulua "github.com/zyedidia/micro/v2/internal/lua"
25 "github.com/zyedidia/micro/v2/internal/screen"
26 "github.com/zyedidia/micro/v2/internal/shell"
27 "github.com/zyedidia/micro/v2/internal/util"
28 "github.com/zyedidia/tcell/v2"
36 flagVersion = flag.Bool("version", false, "Show the version number and information")
37 flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
38 flagOptions = flag.Bool("options", false, "Show all option help")
39 flagDebug = flag.Bool("debug", false, "Enable debug mode (prints debug info to ./log.txt)")
40 flagPlugin = flag.String("plugin", "", "Plugin command")
41 flagClean = flag.Bool("clean", false, "Clean configuration directory")
42 optionFlags map[string]*string
47 fmt.Println("Usage: micro [OPTIONS] [FILE]...")
49 fmt.Println(" \tCleans the configuration directory")
50 fmt.Println("-config-dir dir")
51 fmt.Println(" \tSpecify a custom location for the configuration directory")
52 fmt.Println("[FILE]:LINE:COL (if the `parsecursor` option is enabled)")
53 fmt.Println("+LINE:COL")
54 fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
55 fmt.Println("-options")
56 fmt.Println(" \tShow all option help")
58 fmt.Println(" \tEnable debug mode (enables logging to ./log.txt)")
59 fmt.Println("-version")
60 fmt.Println(" \tShow the version number and information")
62 fmt.Print("\nMicro's plugin's can be managed at the command line with the following commands.\n")
63 fmt.Println("-plugin install [PLUGIN]...")
64 fmt.Println(" \tInstall plugin(s)")
65 fmt.Println("-plugin remove [PLUGIN]...")
66 fmt.Println(" \tRemove plugin(s)")
67 fmt.Println("-plugin update [PLUGIN]...")
68 fmt.Println(" \tUpdate plugin(s) (if no argument is given, updates all plugins)")
69 fmt.Println("-plugin search [PLUGIN]...")
70 fmt.Println(" \tSearch for a plugin")
71 fmt.Println("-plugin list")
72 fmt.Println(" \tList installed plugins")
73 fmt.Println("-plugin available")
74 fmt.Println(" \tList available plugins")
76 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")
77 fmt.Println("-option value")
78 fmt.Println(" \tSet `option` to `value` for this session")
79 fmt.Println(" \tFor example: `micro -syntax off file.c`")
80 fmt.Println("\nUse `micro -options` to see the full list of configuration options")
83 optionFlags = make(map[string]*string)
85 for k, v := range config.DefaultAllSettings() {
86 optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'.", k, v))
92 // If -version was passed
93 fmt.Println("Version:", util.Version)
94 fmt.Println("Commit hash:", util.CommitHash)
95 fmt.Println("Compiled on", util.CompileDate)
100 // If -options was passed
102 m := config.DefaultAllSettings()
104 keys = append(keys, k)
107 for _, k := range keys {
109 fmt.Printf("-%s value\n", k)
110 fmt.Printf(" \tDefault value: '%v'\n", v)
115 if util.Debug == "OFF" && *flagDebug {
120 // DoPluginFlags parses and executes any flags that require LoadAllPlugins (-plugin and -clean)
121 func DoPluginFlags() {
122 if *flagClean || *flagPlugin != "" {
123 config.LoadAllPlugins()
125 if *flagPlugin != "" {
128 config.PluginCommand(os.Stdout, *flagPlugin, args)
129 } else if *flagClean {
137 // LoadInput determines which files should be loaded into buffers
138 // based on the input stored in flag.Args()
139 func LoadInput(args []string) []*buffer.Buffer {
140 // There are a number of ways micro should start given its input
142 // 1. If it is given a files in flag.Args(), it should open those
144 // 2. If there is no input file and the input is not a terminal, that means
145 // something is being piped in and the stdin should be opened in an
148 // 3. If there is no input file and the input is a terminal, an empty buffer
154 buffers := make([]*buffer.Buffer, 0, len(args))
156 btype := buffer.BTDefault
157 if !isatty.IsTerminal(os.Stdout.Fd()) {
158 btype = buffer.BTStdout
161 files := make([]string, 0, len(args))
162 flagStartPos := buffer.Loc{-1, -1}
163 flagr := regexp.MustCompile(`^\+(\d+)(?::(\d+))?$`)
164 for _, a := range args {
165 match := flagr.FindStringSubmatch(a)
166 if len(match) == 3 && match[2] != "" {
167 line, err := strconv.Atoi(match[1])
169 screen.TermMessage(err)
172 col, err := strconv.Atoi(match[2])
174 screen.TermMessage(err)
177 flagStartPos = buffer.Loc{col - 1, line - 1}
178 } else if len(match) == 3 && match[2] == "" {
179 line, err := strconv.Atoi(match[1])
181 screen.TermMessage(err)
184 flagStartPos = buffer.Loc{0, line - 1}
186 files = append(files, a)
192 // We go through each file and load it
193 for i := 0; i < len(files); i++ {
194 buf, err := buffer.NewBufferFromFileAtLoc(files[i], btype, flagStartPos)
196 screen.TermMessage(err)
199 // If the file didn't exist, input will be empty, and we'll open an empty buffer
200 buffers = append(buffers, buf)
202 } else if !isatty.IsTerminal(os.Stdin.Fd()) {
204 // The input is not a terminal, so something is being piped in
205 // and we should read from stdin
206 input, err = ioutil.ReadAll(os.Stdin)
208 screen.TermMessage("Error reading from stdin: ", err)
211 buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
213 // Option 3, just open an empty buffer
214 buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
222 if util.Stdout.Len() > 0 {
223 fmt.Fprint(os.Stdout, util.Stdout.String())
228 // runtime.SetCPUProfileRate(400)
229 // f, _ := os.Create("micro.prof")
230 // pprof.StartCPUProfile(f)
231 // defer pprof.StopCPUProfile()
239 err = config.InitConfigDir(*flagConfigDir)
241 screen.TermMessage(err)
244 config.InitRuntimeFiles()
245 err = config.ReadSettings()
247 screen.TermMessage(err)
249 err = config.InitGlobalSettings()
251 screen.TermMessage(err)
255 for k, v := range optionFlags {
257 nativeValue, err := config.GetNativeValue(k, config.DefaultAllSettings()[k], *v)
259 screen.TermMessage(err)
262 config.GlobalSettings[k] = nativeValue
271 fmt.Println("Fatal: Micro could not initialize a Screen.")
275 c := make(chan os.Signal, 1)
276 signal.Notify(c, syscall.SIGTERM)
281 for _, b := range buffer.OpenBuffers {
287 if screen.Screen != nil {
293 m := clipboard.SetMethod(config.GetGlobalOption("clipboard").(string))
294 clipErr := clipboard.Initialize(m)
297 if err := recover(); err != nil {
298 if screen.Screen != nil {
301 if e, ok := err.(*lua.ApiError); ok {
302 fmt.Println("Lua API error:", e)
304 fmt.Println("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")
306 // backup all open buffers
307 for _, b := range buffer.OpenBuffers {
314 err = config.LoadAllPlugins()
316 screen.TermMessage(err)
319 action.InitBindings()
320 action.InitCommands()
322 err = config.InitColorscheme()
324 screen.TermMessage(err)
327 err = config.RunPluginFn("preinit")
329 screen.TermMessage(err)
336 // No buffers to open
344 err = config.RunPluginFn("init")
346 screen.TermMessage(err)
349 err = config.RunPluginFn("postinit")
351 screen.TermMessage(err)
355 log.Println(clipErr, " or change 'clipboard' option")
358 if a := config.GetGlobalOption("autosave").(float64); a > 0 {
359 config.SetAutoTime(int(a))
360 config.StartAutoSave()
363 screen.Events = make(chan tcell.Event)
365 // Here is the event loop which runs in a separate thread
369 e := screen.Screen.PollEvent()
377 // clear the drawchan so we don't redraw excessively
378 // if someone requested a redraw before we started displaying
379 for len(screen.DrawChan()) > 0 {
383 // wait for initial resize event
385 case event := <-screen.Events:
386 action.Tabs.HandleEvent(event)
387 case <-time.After(10 * time.Millisecond):
388 // time out after 10ms
396 // DoEvent runs the main action loop of the editor
398 var event tcell.Event
400 // Display everything
401 screen.Screen.Fill(' ', config.DefStyle)
402 screen.Screen.HideCursor()
403 action.Tabs.Display()
404 for _, ep := range action.MainTab().Panes {
407 action.MainTab().Display()
408 action.InfoBar.Display()
411 // Check for new events
413 case f := <-shell.Jobs:
414 // If a new job has finished while running in the background we should execute the callback
416 f.Function(f.Output, f.Args)
418 case <-config.Autosave:
420 for _, b := range buffer.OpenBuffers {
424 case <-shell.CloseTerms:
425 case event = <-screen.Events:
426 case <-screen.DrawChan():
427 for len(screen.DrawChan()) > 0 {
434 if action.InfoBar.HasPrompt {
435 action.InfoBar.HandleEvent(event)
437 action.Tabs.HandleEvent(event)