-
Notifications
You must be signed in to change notification settings - Fork 0
/
ecb-eshell.el
394 lines (330 loc) · 16.9 KB
/
ecb-eshell.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
;;; ecb-eshell.el --- eshell integration for the ECB.
;; Copyright (C) 2000 - 2005 Jesper Nordenberg,
;; Klaus Berndl,
;; Kevin A. Burton,
;; Free Software Foundation, Inc.
;; Author: Klaus Berndl <klaus.berndl@sdm.de>
;; Kevin A. Burton <burton@openprivacy.org>
;; Maintainer: Klaus Berndl <klaus.berndl@sdm.de>
;; Keywords: browser, code, programming, tools
;; Created: 2001
;; This program is free software; you can redistribute it and/or modify it under
;; the terms of the GNU General Public License as published by the Free Software
;; Foundation; either version 2, or (at your option) any later version.
;; This program is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
;; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
;; details.
;; You should have received a copy of the GNU General Public License along with
;; GNU Emacs; see the file COPYING. If not, write to the Free Software
;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
;; $Id: ecb-eshell.el,v 1.86 2010/02/23 16:08:56 berndl Exp $
;;; Commentary:
;; This package provides eshell integration for the ECB. This basically allows
;; you to jump to the eshell in the compilation window, sync up the current
;; eshell with the current ECB buffer and run commands without getting in the
;; way.
;;
;; It provides the following features:
;;
;; - ability to jump to the eshell buffer within the compilation window ( C-.e )
;; If the eshell isn't running it will be started
;;
;; - expands the compilation window when you run commands. So for example it
;; allows you to view the eshell in minimized mode and then when you run 'ls'
;; the window automatically expands.
;;
;; - Synchronizes the current directory of the eshell with the current buffer
;; of the either the edit-window or the ecb-windows.
;;
;; - Provides smart window layout of the eshell buffer. This makes sure that
;; the eshell is taking up the exact amount of space and that nothing is
;; hidden.
;;
;; The goal is to make it easy to jump to a command prompt to run OS level
;; commands.
;;
;; If you enjoy this software, please consider a donation to the EFF
;; (http://www.eff.org)
;;; History:
;; For the ChangeLog of this file see the CVS-repository. For a complete
;; history of the ECB-package see the file NEWS.
;;; Design:
;;
;; Syncing the current buffer with the eshell is done two ways. If the buffer
;; is visible in a window, we always resync. If it is not visible then
;; ecb-eshell-goto-eshell will sync up when the user goes to the eshell
;; buffer.
;;
;; Integrating of eshell is mostly done by advicing the command `eshell' which
;; uses the mechanism of the display-buffer (adviced version).
;;; Code:
(eval-when-compile
(require 'silentcomp))
(require 'ecb-util)
(require 'ecb-compilation)
(require 'ecb-common-browser)
(silentcomp-defvar eshell-buffer-name)
(silentcomp-defun eshell)
(silentcomp-defun eshell/cd)
(silentcomp-defun eshell-send-input)
(silentcomp-defun eshell-bol)
(defgroup ecb-eshell nil
"Settings for eshell integration within the ECB."
:group 'ecb
:prefix "ecb-eshell-")
(defcustom ecb-eshell-enlarge-when-eshell t
"*Enlarge the compile-window if it is selected by `eshell'.
This takes only effect if the command `eshell' is called!"
:group 'ecb-eshell
:type 'boolean)
(defcustom ecb-eshell-fit-window-to-command-output t
"*Fit the compile-window after an eshell-command to the output.
This is done by the function `ecb-eshell-fit-window-to-output' which is added
to `eshell-post-command-hook' ie. which is running autom. after each
eshell-command."
:group 'ecb-eshell
:type 'boolean)
(defcustom ecb-eshell-auto-activate nil
"*Startup the eshell and display it in the compile-window.
If current layout does not display a compile-window \(see
`ecb-compile-window-height') then nothing is done."
:group 'ecb-eshell
:type 'boolean)
;; TODO: Klaus Berndl <klaus.berndl@sdm.de>: was ecb-eshell-synchronize -->
;; rename in texi and also to ecb-upgrade (also with value-upgrade!)
(defcustom ecb-eshell-buffer-sync 'basic
"*Synchronize eshell with the default-directory of current source-buffer.
This option takes only effect if a permanant compile-window is used in the
current layout.
If 'always then the synchronization takes place always a buffer
changes in the edit window and if after this the
default-directory of the new edit-buffer is different from the
default-directory of the current eshell-buffer. If value is nil
then never a synchronization will take place. If a list of
major-modes then only if the `major-mode' of the new buffer
belongs NOT to this list.
If the special value 'basic is set then ECB uses the setting of the option
`ecb-basic-buffer-sync'."
:group 'ecb-eshell
:type '(radio :tag "Synchronize the eshell if in compile-window."
(const :tag "Use basic value" :value basic)
(const :tag "Always" :value always)
(const :tag "Never" nil)
(repeat :tag "Not with these modes"
(symbol :tag "mode"))))
(defcustom ecb-eshell-buffer-sync-delay 'basic
"*Time Emacs must be idle before the eshell-buffer of ECB is synchronized.
Synchronizing is done with the current source displayed in the edit window. If
nil then there is no delay, means synchronization takes place immediately. A
small value of about 0.25 seconds saves CPU resources and you get even though
almost the same effect as if you set no delay.
If the special value 'basic is set then ECB uses the setting of the option
`ecb-basic-buffer-sync-delay'."
:group 'ecb-eshell
:type '(radio (const :tag "Use basic value" :value basic)
(const :tag "No synchronizing delay" :value nil)
(number :tag "Idle time before synchronizing" :value 2))
:set (function (lambda (symbol value)
(set symbol value)
(if (and (boundp 'ecb-minor-mode)
ecb-minor-mode)
(ecb-activate-ecb-autocontrol-function
value 'ecb-analyse-buffer-sync))))
:initialize 'custom-initialize-default)
(defvar ecb-eshell-pre-command-point nil
"Point in the buffer we are at before we executed a command.")
(defvar ecb-eshell-buffer-list nil
"List of eshell-buffers created until now.
Background: `eshell' creates new eshell-buffers with `generate-new-buffer' if
called with an prefix arg!")
(defecb-advice-set ecb-eshell-adviced-functions
"These functions of eshell are adviced if ehsell is active during ECB is
active.")
(defecb-advice eshell around ecb-eshell-adviced-functions
"Ensure that ehsell is running in the ECB-compile-window if any."
;; we tell ECB to handle the eshell-buffers as compilation-buffers so they
;; will be displayed in the compile-window (if any). We must add this as
;; regexp because ehsell can open new eshell-buffers with a name created by
;; generate-new-buffer-name! This approach is not completely save because if
;; a users changes `eshell-buffer-name' during acivated ECB we get not
;; informed about this and maybe we can handle the new buffer-name of eshell
;; not as compilation-buffer. But we have no other chance: Adding the return
;; value of `eshell' in the advice to `ecb-compilation-buffer-names-internal'
;; does not help because `eshell' uses the new buffer-name already for
;; `pop-to-buffer'. So an after advice would add the new buffer-name to late
;; and a before-advice does not know the new-buffer name. The only way would
;; be to reimplement the whole `eshell'-code in an around advice but this is
;; not related to the benefit. IMO is it very improbably that a user changes
;; `eshell-buffer-name' at all...
(let ((new-elem (cons (concat ".*"
(regexp-quote eshell-buffer-name)
".*")
t)))
(if ecb-compile-window-height
(progn
(add-to-list 'ecb-compilation-buffer-names-internal new-elem)
(add-to-list 'ecb-compilation-major-modes-internal 'eshell-mode))
;; if we have no persistent compile-window we do not handle eshell autom.
;; as compilation-buffer. If the user wants this then he has to modify
;; `ecb-compilation-buffer-names' and/or `ecb-compilation-major-modes'.
;; Therefore we remove the new-elem here from the internal lists.
(setq ecb-compilation-buffer-names-internal
(delete new-elem ecb-compilation-buffer-names-internal))
(setq ecb-compilation-major-modes-internal
(delete 'eshell-mode ecb-compilation-major-modes-internal))))
;; maybe we have to auto toggle our compile-window if temporally hidden
(when (equal 'hidden (ecb-compile-window-state))
(ecb-layout-debug-error "eshell around-advice: comp-win will be toggled.")
(ecb-toggle-compile-window 1))
(ecb-activate-ecb-autocontrol-function ecb-eshell-buffer-sync-delay
'ecb-eshell-buffer-sync)
;; some hooks
(add-hook 'eshell-post-command-hook 'ecb-eshell-recenter)
(add-hook 'eshell-post-command-hook 'ecb-eshell-fit-window-to-output)
(add-hook 'eshell-pre-command-hook 'ecb-eshell-precommand-hook)
(add-hook 'window-size-change-functions 'ecb-eshell-window-size-change)
;; run `eshell' --------------------------------------------
(ecb-eshell-save-buffer-history
ad-do-it)
;; ---------------------------------------------------------
;; some post processing
;; add the buffer of the buffer used/created by `eshell' to
;; `ecb-eshell-buffer-list'
(add-to-list 'ecb-eshell-buffer-list ad-return-value)
(when ecb-eshell-enlarge-when-eshell
(ecb-toggle-compile-window-height 1))
;;always recenter because if the point is at the top of the eshell buffer
;;and we switch to it the user is not going to be able to type a command
;;right away.
(ecb-eshell-recenter)
;;sync to the current buffer
(ecb-eshell-buffer-sync))
(defun ecb-eshell-activate-integration ()
"Does all necessary to activate the eshell-integration. But this doesn not
load or activate eshell - it just prepares ECB to work perfectly with eshell."
(ecb-enable-advices 'ecb-eshell-adviced-functions))
(defun ecb-eshell-deactivate-integration ()
(ecb-disable-advices 'ecb-eshell-adviced-functions)
(ecb-stop-autocontrol/sync-function 'ecb-eshell-buffer-sync)
(remove-hook 'eshell-post-command-hook 'ecb-eshell-recenter)
(remove-hook 'eshell-post-command-hook 'ecb-eshell-fit-window-to-output)
(remove-hook 'eshell-pre-command-hook 'ecb-eshell-precommand-hook)
(remove-hook 'window-size-change-functions 'ecb-eshell-window-size-change))
(defecb-autocontrol/sync-function ecb-eshell-buffer-sync nil ecb-eshell-buffer-sync t
"Synchronize the eshell with the directory of current source-buffer.
This is only done if the eshell is currently visible in the compile-window of
ECB and if either this function is called interactively or
`ecb-eshell-buffer-sync' is not nil."
(when (and (equal (selected-frame) ecb-frame)
(ecb-compile-window-live-p)
(ecb-point-in-edit-window-number))
(let* ((my-eshell-buffer
;; nil or a living eshell-buffer in the ecb-compile-window
(car (member (window-buffer ecb-compile-window)
ecb-eshell-buffer-list)))
(my-reference-directory default-directory)
(my-eshell-directory (and (bufferp my-eshell-buffer)
(with-current-buffer my-eshell-buffer
default-directory))))
(when (and (bufferp my-eshell-buffer)
(stringp my-reference-directory)
(stringp my-eshell-directory)
(not (ecb-string= (ecb-fix-filename my-reference-directory)
(ecb-fix-filename my-eshell-directory))))
(ecb-eshell-save-buffer-history
(with-current-buffer my-eshell-buffer
;; make sure we have a clean eshell-command-line
(goto-char (point-max))
(eshell-bol)
(delete-region (point) (point-at-eol))
;;change the directory without showing the cd command
(eshell/cd my-reference-directory))
;;execute the command
(save-selected-window
(select-window ecb-compile-window)
(eshell-send-input)))
(ecb-eshell-recenter)
;; we need to make sure that that the eshell buffer isn't at the
;; top of the buffer history list just because we implicitly
;; changed its directory and switched to it. It might not be a
;; good idea in the long term to put it all the way at the end of
;; the history list but it is better than leaving it at the top.
(bury-buffer eshell-buffer-name)))))
(defmacro ecb-eshell-save-buffer-history (&rest body)
"Protect the buffer-list so that the eshell buffer name is not placed early
in the buffer list or at all if it currently doesn't exist."
(let ((eshell-buffer-list (make-symbol "my-buffer-list")))
`(let ((,eshell-buffer-list (ecb-frame-parameter (selected-frame)
'buffer-list)))
(unwind-protect
(progn
,@body)
(modify-frame-parameters nil (list (cons 'buffer-list
,eshell-buffer-list)))))))
(defun ecb-eshell-recenter(&optional display-errors)
"Recenter the eshell window so that the prompt is at the buffer-end."
(interactive (list t))
(if (and (equal (selected-frame) ecb-frame)
(ecb-compile-window-live-p)
;; the buffer in the ecb-compile-window is a living eshell-buffer
(member (window-buffer ecb-compile-window)
ecb-eshell-buffer-list))
(save-selected-window
(select-window ecb-compile-window)
(goto-char (point-max))
(recenter -2))
(when display-errors
(ecb-error "Eshell not running or compile-window not visible!"))))
(defun ecb-eshell-precommand-hook ()
;;use the eshell-pre-command-hook to set the point.
(setq ecb-eshell-pre-command-point (point)))
(defun ecb-eshell-fit-window-to-output()
"Fit window of eshell to the output of last command. This function is added
to `eshell-post-command-hook' and only called there. This function tries to
fit the height of the compile-window best to the last command-output. The
algorithm fit the window to the height of the last command-output but do not
enlarge the compile-window over half of the frame-height and also not below
`ecb-compile-window-height' (in lines)."
(when (and (equal (selected-frame) ecb-frame)
(ecb-compile-window-live-p)
;; the buffer in the ecb-compile-window is a living eshell-buffer
(member (window-buffer ecb-compile-window)
ecb-eshell-buffer-list))
;; fit the window to the height of the last command-output but do not
;; enlarge the compile-window over half of the frame-height and also not
;; below `ecb-compile-window-height' (in lines).
(when (and ecb-eshell-fit-window-to-command-output
(integer-or-marker-p ecb-eshell-pre-command-point))
(let* ((compile-window-height-lines
(ecb-normalize-number ecb-compile-window-height
(1- (frame-height))))
(ecb-enlarged-compilation-window-max-height
(max (min (with-current-buffer (window-buffer ecb-compile-window)
;; we want to see the old command line too and 2
;; must be added because we have a modeline and one
;; empty line cause of the (recenter -2) in
;; `ecb-eshell-recenter'. For XEmacs it would be
;; better to check if a horiz. scrollbar is used.
;; This causes the one line more we need for XEmacs
(+ (if ecb-running-xemacs 4 3)
(count-lines ecb-eshell-pre-command-point
(point))))
(/ (1- (frame-height)) 2))
compile-window-height-lines)))
(ecb-toggle-compile-window-height 1)
(ecb-eshell-recenter))
;;reset
(setq ecb-eshell-pre-command-point nil))))
(defun ecb-eshell-auto-activate-hook()
"Activate the eshell when ECB is activated. See `ecb-eshell-auto-activate'."
(when ecb-eshell-auto-activate
(ignore-errors (eshell))))
(defun ecb-eshell-window-size-change(frame)
"Called when we change window sizes so that the eshell can resize."
(when (and ecb-minor-mode
(equal frame ecb-frame))
(ignore-errors (ecb-eshell-recenter))))
(add-hook 'ecb-activate-hook 'ecb-eshell-auto-activate-hook)
(silentcomp-provide 'ecb-eshell)
;;; ecb-eshell.el ends here