Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to extend and customize the Shell Environment in Emacs

2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly shows you "how to expand and customize the Shell environment in Emacs". It is easy to understand and clear. I hope it can help you solve your doubts. Let me lead you to study and learn how to expand and customize the Shell environment in Emacs.

Enter and exit Shell Mode

Gently I go, as I gently come; I gently wave goodbye to the clouds of the western sky.

But in the default design of Emacs Shell Mode, there is no such easy and elegant way to enter and exit. This is where we will customize and extend in this section.

The entry of Shell buffer

The first is to enter. In the * * section of this article, there is a tip on how to open multiple Shell buffer in GNU Emacs-we need to rename the existing Shell buffer before we can open a Shell buffer called * shell* again. This is the default name that Emacs uses when creating Shell buffer.

This is a very inelegant behavior. Such details should be taken care of by Emacs in advance. All I need is an elegant entry. There are two ways to achieve this, one is to change the Shell buffer to a unique name when it is created, and the other is to automatically change the name of the Shell buffer according to the user's usage after the Shell buffer is created. Due to the characteristics of my work, I chose the second option.

In my work environment, I have to log on to a remote machine to work most of the time. So I very much hope that the name of Shell buffer can be automatically changed to the name of the target machine I logged in, so that when I log on to a large number of machines to operate, I can easily distinguish by the buffer name. That's why I chose the second plan. I first accept the default buffer created by Emacs, and then Emacs automatically renames me when I log in to the remote machine. If I am not logged in to the remote machine, it will remain the default name, or I will actively modify the buffer name.

There is an added benefit of accepting the default buffer name-when you open a large number of buffer to work, if you want to go back to the default Shell buffer, you don't have to switch in the long buffer list, just execute a command to open Shell, that is, MMux shell,Emacs will immediately bring you to the default Shell buffer. In order to open Shell more conveniently, I bound this command to the Cmurc z key combination:

(global-set-key (kbd "Cmerc z") (quote shell))

Now let's take a look at how Emacs automatically changes the name of Shell buffer when I log in to a remote machine. To achieve this, you first need to write a rename-buffer-in-ssh-login function:

Listing 1. Rename-buffer-in-ssh-login function

(defun rename-buffer-in-ssh-login (cmd) "Rename buffer to the destination hostname in ssh login" (if (string-match "ssh [- _ a-z0-9A-Z] + @ [- _ a-z0-9A-Z.] + [] * [^-_ a-z0-9-A-Z] * $" cmd) (let ((host (nth 2 (split-string cmd "[@\ n]" t) (rename-buffer (concat "*" host) (add-to-list 'shell-buffer-name-list (concat "*" host)); (message "% s" shell-buffer-name-list)

This function parses the commands provided to it. If the predefined regular expression is matched, the machine name after the @ character is intercepted, and then the name of the current buffer is modified using the rename-buffer command. In addition, since Shell buffer is treated as a temporary buffer in the default convention of GNU Emacs, and the name of a temporary buffer usually begins with a * character, this naming convention is still followed, adding a * prefix to the machine name.

To make this function work, we need to add it to a hook variable comint-input-filter-functions.

(add-hook 'comint-input-filter-functions' rename-buffer-in-ssh-login)

Comint-input-filter-functions is a hook of comint-mode. Shell-mode is actually derived from comint-mode, so comint-mode 's hook also works in Shell-mode.

Comint-mode or Shell-mode first runs the function in comint-input-filter-functions hook before passing the command entered into buffer to the background process (in this case, the Shell process) for execution, passing the entered command to the function as an argument. So our rename-buffer-in-ssh-login function can track every command entered into buffer and perform a predetermined operation when a command like ssh msg@hostA.cn.ibm.com or ssh msg@hostB is found. At the same time, the design of regular expressions also avoids the opportunity of misaction on remote commands such as ssh msg@hostA.cn.ibm.com ls / opt/IBM that are not for login purposes.

Careful readers who see here may have noticed a detail that two lines have been commented out of the above code. In particular, the * * line adds the intercepted machine name to a shell-buffer-name-list list. In fact, this code exists to track the change of the Shell buffer name, and then work with another function, rename-buffer-in-ssh-exit, to change the name of the Shell buffer back to its original appearance every time you log out of ssh. However, due to the complexity of practical application, a very satisfactory implementation scheme has not been found so far. Interested readers can try to implement this function on their own.

The exit of Shell buffer

Now that the problem of entry is solved, let's take a look at what will happen when we exit.

When the user exits the Shell session, Emacs does not delete the Shell buffer, but leaves it there for further processing by the user.

Dove@bash-4.1$ exitexitProcess shell finished

If the user executes the Mmurx shell command again at this time, Emacs will reuse the buffer again.

Dove@bash-4.1$ dove@bash-4.1$ exitexitProcess shell finisheddove@bash-4.1 $

First of all, this is actually a very correct design. Because the content in Shell buffer is usually very important. Sometimes I even save some Shell buffer as a file at the end of the day for future reference. This is not only a record of all the commands executed during the day, but also the output of all these commands, even when I logged on to several different machines to perform different operations, and all this work was recorded in this Shell buffer. It can be said that this buffer is the record of all my footprints since the day. Just imagine, where else can such a complete and detailed work record be provided? Is there any other place that can provide such a convenient search function? Even the output of the command can be searched at will?

However, I soon got used to handling my Shell buffer correctly. I'm used to saving the main buffer before quitting, so can I tell Emacs not to be so squeamish at this time? As a matter of fact, this is really a difficult thing to do. I have tried to use comint-output-filter-functionshook to capture information like Process shell finished, but this information is output by Emacs only after comint-mode has exited, so it can't be captured in this hook at all.

It wasn't until one day when I was looking through the Emacs source code that I suddenly saw the function set-process-sentinel to find a solution. The set-process-sentinel function can set a "sentry" for a specific process, and when the state of the process changes (for example, when the process ends), the "sentry" will inform Emacs to call the appropriate function to complete the scheduled work. With this solution, we just need to associate the function that deletes Shell buffer to the correct process.

Here are these two functions:

Listing 2. Two functions

(defun kill-shell-buffer (process event) "The one actually kill shell buffer when exit." (kill-buffer (process-buffer process) (defun kill-shell-buffer-after-exit () "kill shell buffer when exit." (set-process-sentinel (get-buffer-process (current-buffer)) # 'kill-shell-buffer))

The function of kill-shell-buffer is to delete the buffer; kill-shell-buffer-after-exit function corresponding to the process, and the function is to associate the kill-shell-buffer function to the correct process. Then when we add this function to shell-mode-hook, we can get the correct process information every time we open Shell buffer.

(add-hook 'shell-mode-hook' kill-shell-buffer-after-exit t)

Outline in Shell Mode

In this section we talk about outline-mode. Outline-mode is a very useful writing mode for GNU Emacs. Using outline-mode, you can easily manipulate structured documents, expand the contents of documents, or hide them step by step, which can not only take charge of the overall situation, but also go deep into the details. Outline-mode is so wonderful that Professor Carsten Dominik developed a powerful orgmode on this basis.

In this section we will discuss how to apply the power of outline-mode to Shell-mode. Before we get into the details, let's give a brief introduction to Outline-mode.

In Outline mode, the content in a document is divided into two structures, one is "title" and the other is "content". The "title" can be divided into different levels according to the needs. The collapse and expansion of the contents of a document is based on the level of these "headings". For example, the following is an example from GNU Emacs Manual:

* FoodThis is the body,which says something about the topic of food.** Delicious FoodThis is the body of the second-level header.** Distasteful FoodThis could havea body too, withseveral lines.*** Dormitory Food* ShelterAnother first-level topic with its header line.

When we fold this document, it can be folded into this form.

* Food...* Shelter...

Or in this form.

* Food...** Delicious Food...** Distasteful Food* Shelter...

Or we can expand Delicious Food separately.

* Food...** Delicious FoodThis is the body of the second-level header.** Distasteful Food* Shelter...

So what do these examples have to do with Shell mode? If we think of the * commands * in Shell buffer as the "title" of outline-mode and the output generated by the command as "content", can we fold all Shell commands like a normal structured document? As the following example shows:

Listing 3. Example

Dove@bash-4.1$ cd ~ / org...2: 2001: 11:23:10: ~ / orgdove@bash-4.1$ ls * .elcalendar-setup.el dove-ext.el org-mode.el settings.elcolor-theme.el keybindings.el plugins.eldove@bash-4.1$ ee work.org & ..dove@bash-4.1$ Waiting for Emacs...dove@bash-4.1$ ls...dove@bash-4.1$ ee settings.el &. Dove@bash -4. 1$ Waiting for Emacs...dove@bash-4.1$ cd~/...dove@bash-4.1$ ls...dove@bash-4.1 $...

When we fold all the contents of the Shell buffer, we see a timeline. You can not only summarize the whole history at a glance, but also delve into the details of any command at any time. Such a scenario is more like a "time machine" than a history command that only tells us what we have done.

So how to realize such a dream? The key is to make outline-mode recognize our "title". In outline-mode, the default "title" is a *, which starts with a match of * * characters on a line of text. The match is "title", and the one that does not match is "content". The more times you match, the lower the level of "title". We can define our own "title" by setting the value of the outline-regexp variable. One possible solution in Shell mode is to define the content of the Shell prompt as "title". As in the following example:

(setq outline-regexp ". * [bB] ash.* [#\ $]")

After setting the title, type Mmurx outline-minor-mode in Shell mode and you can enjoy the convenience brought by outline-mode. For example, the result shown in the above example can be achieved using the following three operations:

Enter the Mmurx hide-body or Mmerx hide-all commands to collapse all the commands in the Shell buffer

Move the cursor to the line where ls * .el is located

Expand the ls * .el command using the MMI x show-entry or MMI x show-subtree commands

Enhanced outline in Shell Mode

In the previous section, I talked about how to make outline-minor-mode work in shell-mode by setting the outline-regexp variable, but it's hard to avoid some negative effects with such a simple setting. Because the outline-regexp variable is a global variable, the value of outline-regexp is bound to change the behavior of outline-minor-mode in other modes, which is certainly not what you want.

So what I actually use in my work is a relatively complex approach: use a function to set a separate outline-regexp for each buffer, and change the outline-regexp variable to a local variable within a specific buffer range. Here is the function:

Listing 4. Set the function of buffer

(defun set-outline-minor-mode-regexp () "(let ((find-regexp (lambda (lst mode)" (let ((innerList (car lst) (if innerList (if (string= (car innerList) mode) (car (cdr innerList)) (progn (pop lst) (funcall find-regexp lst mode) (outline-minor-mode 1) (make-local-variable 'outline-regexp) (setq outline-regexp (funcall find-regexp outline-minor-mode-list major-mode)

This function first defines an anonymous function, which is stored in the find-regexp variable, which recursively iterates through a nested list until the value corresponding to the given pattern is found, then launches outline-minor-mode, modifies outline-regexp to a local variable, and then calls the anonymous function above to set the correct outline-regexp.

For this function to work, we need to add it to the hook of each main mode, as shown in the following example:

Listing 5. Example

(add-hook 'shell-mode-hook' set-outline-minor-mode-regexp t) (add-hook 'sh-mode-hook' set-outline-minor-mode-regexp t) (add-hook 'emacs-lisp-mode-hook' set-outline-minor-mode-regexp t) (add-hook 'perl-mode-hook' set-outline-minor-mode-regexp t)

But careful readers should see that this set-outline-minor-mode-regexp function does not take any arguments because these main patterns do not pass any parameters to them when they call the hook function. So where does the data we need come from? Obviously, you need a global variable outline-minor-mode-list to store all the data needed by the set-outline-minor-mode-regexp function.

Listing 6. Global variable outline-minor-mode-list

(setq outline-minor-mode-list (list'(emacs-lisp-mode "(defun")'(shell-mode ". * [bB] ash.* [#\ $]")'(sh-mode "function. * {")'(perl-mode "sub")

With these extensions, Emacs can set the correct outline-regexp value for the buffer when creating a new buffer.

Extended reading of hook

Some readers may have noticed that the concept of hook has been mentioned many times in this article, so what on earth is hook? What role did he play in Emacs? Here I would like to give you a brief introduction.

Simply put, hook is a Lisp variable that stores a list of functions, and each function in the list is called a hook function of the hook. Many major modes of GNU Emacs try to find and call the hook function in the hook variable of the corresponding pattern after initialization. Therefore, hook has become a very important mechanism in the process of customizing Emacs. We can easily customize or extend the behavior of Emacs by adding hook functions.

The simplest use of hook is to call existing Emacs functions directly, such as starting a specific submode (minor modes):

(add-hook 'shell-mode-hook' outline-minor-mode t)

A more complex usage, as shown above, is to write your own hook function.

There are a few details to pay attention to about hook

The names of most ordinary hook variables are formed by adding a-hook suffix to the name of the main schema.

However, not all hook variables are named this way

Most ordinary hook functions will not pass any parameters to it when they are called, and they will not care about the return result of the function.

However, not all hook functions are called this way

Loaded hook functions cannot be overridden or modified by executing add-hook again. The actual result will load multiple versions of the hook function. One solution is to clear the hook variable and load it again:

(setq 'shell-mode-hook nil) (add-hook' shell-mode-hook 'outline-minor-mode t) the above is all the content of this article "how to expand and customize the Shell environment in Emacs". Thank you for reading! I believe you all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report