12 var commands map[string]func([]string)
14 var commandActions = map[string]func([]string){
23 // InitCommands initializes the default commands
25 commands = make(map[string]func([]string))
27 defaults := DefaultCommands()
28 parseCommands(defaults)
31 func parseCommands(userCommands map[string]string) {
32 for k, v := range userCommands {
37 // MakeCommand is a function to easily create new commands
38 // This can be called by plugins in Lua so that plugins can define their own commands
39 func MakeCommand(name, function string) {
40 action := commandActions[function]
41 if _, ok := commandActions[function]; !ok {
42 // If the user seems to be binding a function that doesn't exist
43 // We hope that it's a lua function that exists and bind it to that
44 action = LuaFunctionCommand(function)
47 commands[name] = action
50 // DefaultCommands returns a map containing micro's default commands
51 func DefaultCommands() map[string]string {
52 return map[string]string{
63 func Set(args []string) {
64 // Set an option and we have to set it for every view
65 for _, tab := range tabs {
66 for _, view := range tab.views {
72 // Bind creates a new keybinding
73 func Bind(args []string) {
75 messenger.Error("Incorrect number of arguments")
78 BindKey(args[0], args[1])
81 // Run runs a shell command in the background
82 func Run(args []string) {
83 // Run a shell command in the background (openTerm is false)
84 HandleShellCommand(strings.Join(args, " "), false)
87 // Quit closes the main view
88 func Quit(args []string) {
89 // Close the main view
93 // Save saves the buffer in the main view
94 func Save(args []string) {
99 // Replace runs search and replace
100 func Replace(args []string) {
101 // This is a regex to parse the replace expression
102 // We allow no quotes if there are no spaces, but if you want to search
103 // for or replace an expression with spaces, you can add double quotes
104 r := regexp.MustCompile(`"[^"\\]*(?:\\.[^"\\]*)*"|[^\s]*`)
105 replaceCmd := r.FindAllString(strings.Join(args, " "), -1)
106 if len(replaceCmd) < 2 {
107 // We need to find both a search and replace expression
108 messenger.Error("Invalid replace statement: " + strings.Join(args, " "))
113 if len(replaceCmd) == 3 {
114 // The user included some flags
115 flags = replaceCmd[2]
118 search := string(replaceCmd[0])
119 replace := string(replaceCmd[1])
121 // If the search and replace expressions have quotes, we need to remove those
122 if strings.HasPrefix(search, `"`) && strings.HasSuffix(search, `"`) {
123 search = search[1 : len(search)-1]
125 if strings.HasPrefix(replace, `"`) && strings.HasSuffix(replace, `"`) {
126 replace = replace[1 : len(replace)-1]
129 // We replace all escaped double quotes to real double quotes
130 search = strings.Replace(search, `\"`, `"`, -1)
131 replace = strings.Replace(replace, `\"`, `"`, -1)
132 // Replace some things so users can actually insert newlines and tabs in replacements
133 replace = strings.Replace(replace, "\\n", "\n", -1)
134 replace = strings.Replace(replace, "\\t", "\t", -1)
136 regex, err := regexp.Compile(search)
138 // There was an error with the user's regex
139 messenger.Error(err.Error())
147 match := regex.FindStringIndex(view.Buf.String())
152 if strings.Contains(flags, "c") {
153 // The 'check' flag was used
154 Search(search, view, true)
156 if settings["syntax"].(bool) {
157 view.matches = Match(view)
160 choice, canceled := messenger.YesNoPrompt("Perform replacement? (y,n)")
162 if view.Cursor.HasSelection() {
163 view.Cursor.Loc = view.Cursor.CurSelection[0]
164 view.Cursor.ResetSelection()
170 view.Cursor.DeleteSelection()
171 view.Buf.Insert(FromCharPos(match[0], view.Buf), replace)
172 view.Cursor.ResetSelection()
175 if view.Cursor.HasSelection() {
176 searchStart = ToCharPos(view.Cursor.CurSelection[1], view.Buf)
178 searchStart = ToCharPos(view.Cursor.Loc, view.Buf)
183 view.Buf.Replace(FromCharPos(match[0], view.Buf), FromCharPos(match[1], view.Buf), replace)
186 view.Cursor.Relocate()
189 messenger.Message("Replaced ", found, " occurences of ", search)
190 } else if found == 1 {
191 messenger.Message("Replaced ", found, " occurence of ", search)
193 messenger.Message("Nothing matched ", search)
197 // RunShellCommand executes a shell command and returns the output/error
198 func RunShellCommand(input string) (string, error) {
199 inputCmd := strings.Split(input, " ")[0]
200 args := strings.Split(input, " ")[1:]
202 cmd := exec.Command(inputCmd, args...)
203 outputBytes := &bytes.Buffer{}
204 cmd.Stdout = outputBytes
205 cmd.Stderr = outputBytes
207 err := cmd.Wait() // wait for command to finish
208 outstring := outputBytes.String()
209 return outstring, err
212 // HandleShellCommand runs the shell command
213 // The openTerm argument specifies whether a terminal should be opened (for viewing output
214 // or interacting with stdin)
215 func HandleShellCommand(input string, openTerm bool) {
216 inputCmd := strings.Split(input, " ")[0]
218 // Simply run the command in the background and notify the user when it's done
219 messenger.Message("Running...")
221 output, err := RunShellCommand(input)
222 totalLines := strings.Split(output, "\n")
224 if len(totalLines) < 3 {
226 messenger.Message(inputCmd, " exited without error")
228 messenger.Message(inputCmd, " exited with error: ", err, ": ", output)
231 messenger.Message(output)
233 // We have to make sure to redraw
237 // Shut down the screen because we're going to interact directly with the shell
241 args := strings.Split(input, " ")[1:]
243 // Set up everything for the command
244 cmd := exec.Command(inputCmd, args...)
246 cmd.Stdout = os.Stdout
247 cmd.Stderr = os.Stderr
249 // This is a trap for Ctrl-C so that it doesn't kill micro
250 // Instead we trap Ctrl-C to kill the program we're running
251 c := make(chan os.Signal, 1)
252 signal.Notify(c, os.Interrupt)
263 // This is just so we don't return right away and let the user press enter to return
266 // Start the screen back up
271 // HandleCommand handles input from the user
272 func HandleCommand(input string) {
273 inputCmd := strings.Split(input, " ")[0]
274 args := strings.Split(input, " ")[1:]
276 if _, ok := commands[inputCmd]; !ok {
277 messenger.Error("Unkown command ", inputCmd)
279 commands[inputCmd](args)