Commit 99b3d64e authored by Stefan Monnier's avatar Stefan Monnier

Eshell: Mostly cosmetic changes to be more explicit about dynbind

* lisp/eshell/em-dirs.el (eshell-dirs-initialize): Reveal the lambdas.
(eshell-expand-multiple-dots): Fix ^+$ => \`+\' in the regexp.

* lisp/eshell/esh-cmd.el (eshell-this-command-hook): Declare as
dynamically scoped.
(eshell-trap-errors): Use `mapc funcall` since this can't have
global/local settings like a true hook.
(eshell-do-eval): Split the `let` case from the rest so we can use
`cl-progv` rather than `eval` for it.
(eshell/which): Use `fboundp` test instead of ugly
gymnastics to try and hide the function call from the compiler.

* lisp/eshell/esh-var.el (eshell-variable-aliases-list): Reveal the lambdas.
(eshell-parse-variable-ref): Use backquotes.
parent 9efc43f3
Pipeline #1037 failed with stage
in 60 minutes and 1 second
......@@ -62,12 +62,11 @@ they lack somewhat in feel from the typical shell equivalents."
(defcustom eshell-dirs-load-hook nil
"A hook that gets run when `eshell-dirs' is loaded."
:version "24.1" ; removed eshell-dirs-initialize
:type 'hook
:group 'eshell-dirs)
:type 'hook)
(defcustom eshell-pwd-convert-function (if (eshell-under-windows-p)
'expand-file-name
'identity)
#'expand-file-name
#'identity)
"The function used to normalize the value of Eshell's `pwd'.
The value returned by `pwd' is also used when recording the
last-visited directory in the last-dir-ring, so it will affect the
......@@ -75,8 +74,7 @@ form of the list used by `cd ='."
:type '(radio (function-item file-truename)
(function-item expand-file-name)
(function-item identity)
(function :tag "Other"))
:group 'eshell-dirs)
(function :tag "Other")))
(defcustom eshell-ask-to-save-last-dir 'always
"Determine if the last-dir-ring should be automatically saved.
......@@ -88,63 +86,53 @@ If set to t, always ask if any Eshell buffers are open at exit time.
If set to `always', the list-dir-ring will always be saved, silently."
:type '(choice (const :tag "Never" nil)
(const :tag "Ask" t)
(const :tag "Always save" always))
:group 'eshell-dirs)
(const :tag "Always save" always)))
(defcustom eshell-cd-shows-directory nil
"If non-nil, using `cd' will report the directory it changes to."
:type 'boolean
:group 'eshell-dirs)
:type 'boolean)
(defcustom eshell-cd-on-directory t
"If non-nil, do a cd if a directory is in command position."
:type 'boolean
:group 'eshell-dirs)
:type 'boolean)
(defcustom eshell-directory-change-hook nil
"A hook to run when the current directory changes."
:type 'hook
:group 'eshell-dirs)
:type 'hook)
(defcustom eshell-list-files-after-cd nil
"If non-nil, call \"ls\" with any remaining args after doing a cd.
This is provided for convenience, since the same effect is easily
achieved by adding a function to `eshell-directory-change-hook' that
calls \"ls\" and references `eshell-last-arguments'."
:type 'boolean
:group 'eshell-dirs)
:type 'boolean)
(defcustom eshell-pushd-tohome nil
"If non-nil, make pushd with no arg behave as `pushd ~' (like `cd').
This mirrors the optional behavior of tcsh."
:type 'boolean
:group 'eshell-dirs)
:type 'boolean)
(defcustom eshell-pushd-dextract nil
"If non-nil, make \"pushd +n\" pop the nth dir to the stack top.
This mirrors the optional behavior of tcsh."
:type 'boolean
:group 'eshell-dirs)
:type 'boolean)
(defcustom eshell-pushd-dunique nil
"If non-nil, make pushd only add unique directories to the stack.
This mirrors the optional behavior of tcsh."
:type 'boolean
:group 'eshell-dirs)
:type 'boolean)
(defcustom eshell-dirtrack-verbose t
"If non-nil, show the directory stack following directory change.
This is effective only if directory tracking is enabled."
:type 'boolean
:group 'eshell-dirs)
:type 'boolean)
(defcustom eshell-last-dir-ring-file-name
(expand-file-name "lastdir" eshell-directory-name)
"If non-nil, name of the file to read/write the last-dir-ring.
See also `eshell-read-last-dir-ring' and `eshell-write-last-dir-ring'.
If it is nil, the last-dir-ring will not be written to disk."
:type 'file
:group 'eshell-dirs)
:type 'file)
(defcustom eshell-last-dir-ring-size 32
"If non-nil, the size of the directory history ring.
......@@ -164,13 +152,11 @@ directories gets pushed, and its size is unlimited.
explicitly very much, but every once in a while would like to return to
a previously visited directory without having to type in the whole
thing again."
:type 'integer
:group 'eshell-dirs)
:type 'integer)
(defcustom eshell-last-dir-unique t
"If non-nil, `eshell-last-dir-ring' contains only unique entries."
:type 'boolean
:group 'eshell-dirs)
:type 'boolean)
;;; Internal Variables:
......@@ -189,20 +175,22 @@ Thus, this does not include the current directory.")
(setq eshell-variable-aliases-list
(append
eshell-variable-aliases-list
'(("-" (lambda (indices)
(if (not indices)
(unless (ring-empty-p eshell-last-dir-ring)
(expand-file-name
(ring-ref eshell-last-dir-ring 0)))
(expand-file-name
(eshell-apply-indices eshell-last-dir-ring indices)))))
("+" "PWD")
("PWD" (lambda (indices)
(expand-file-name (eshell/pwd))) t)
("OLDPWD" (lambda (indices)
`(("-" (lambda (indices)
(if (not indices)
(unless (ring-empty-p eshell-last-dir-ring)
(expand-file-name
(ring-ref eshell-last-dir-ring 0)))) t))))
(ring-ref eshell-last-dir-ring 0)))
(expand-file-name
(eshell-apply-indices eshell-last-dir-ring indices)))))
("+" "PWD")
("PWD" ,(lambda (_indices)
(expand-file-name (eshell/pwd)))
t)
("OLDPWD" ,(lambda (_indices)
(unless (ring-empty-p eshell-last-dir-ring)
(expand-file-name
(ring-ref eshell-last-dir-ring 0))))
t))))
(when eshell-cd-on-directory
(make-local-variable 'eshell-interpreter-alist)
......@@ -213,14 +201,14 @@ Thus, this does not include the current directory.")
eshell-interpreter-alist)))
(add-hook 'eshell-parse-argument-hook
'eshell-parse-user-reference nil t)
#'eshell-parse-user-reference nil t)
(if (eshell-under-windows-p)
(add-hook 'eshell-parse-argument-hook
'eshell-parse-drive-letter nil t))
#'eshell-parse-drive-letter nil t))
(when (eshell-using-module 'eshell-cmpl)
(add-hook 'pcomplete-try-first-hook
'eshell-complete-user-reference nil t))
#'eshell-complete-user-reference nil t))
(make-local-variable 'eshell-dirstack)
(make-local-variable 'eshell-last-dir-ring)
......@@ -230,9 +218,9 @@ Thus, this does not include the current directory.")
(unless eshell-last-dir-ring
(setq eshell-last-dir-ring (make-ring eshell-last-dir-ring-size)))
(add-hook 'eshell-exit-hook 'eshell-write-last-dir-ring nil t)
(add-hook 'eshell-exit-hook #'eshell-write-last-dir-ring nil t)
(add-hook 'kill-emacs-hook 'eshell-save-some-last-dir))
(add-hook 'kill-emacs-hook #'eshell-save-some-last-dir))
(defun eshell-save-some-last-dir ()
"Save the list-dir-ring for any open Eshell buffers."
......@@ -309,11 +297,9 @@ Thus, this does not include the current directory.")
(not (and (eshell-under-windows-p)
(string-match "\\`[A-Za-z]:[\\/]\\'" path))))
(setq path (substring path 0 (1- (length path)))))
(if eshell-pwd-convert-function
(funcall eshell-pwd-convert-function path)
path)))
(funcall (or eshell-pwd-convert-function #'identity) path)))
(defun eshell-expand-multiple-dots (path)
(defun eshell-expand-multiple-dots (filename)
;; FIXME: This advice recommendation is rather odd: it's somewhat
;; dangerous and it claims not to work with minibuffer-completion, which
;; makes it much less interesting.
......@@ -326,16 +312,17 @@ in the minibuffer:
(advice-add 'expand-file-name :around #'my-expand-multiple-dots)
(defun my-expand-multiple-dots (orig-fun filename &rest args)
(apply orig-fun (eshell-expand-multiple-dots filename) args))"
(while (string-match "\\(?:^\\|/\\)\\.\\.\\(\\.+\\)\\(?:$\\|/\\)" path)
(let* ((extra-dots (match-string 1 path))
(while (string-match "\\(?:\\`\\|/\\)\\.\\.\\(\\.+\\)\\(?:\\'\\|/\\)"
filename)
(let* ((extra-dots (match-string 1 filename))
(len (length extra-dots))
replace-text)
(while (> len 0)
(setq replace-text (concat replace-text "/..")
len (1- len)))
(setq path
(replace-match replace-text t t path 1))))
path)
(setq filename
(replace-match replace-text t t filename 1))))
filename)
(defun eshell-find-previous-directory (regexp)
"Find the most recent last-dir matching REGEXP."
......
......@@ -122,24 +122,20 @@ however."
(defcustom eshell-prefer-lisp-functions nil
"If non-nil, prefer Lisp functions to external commands."
:type 'boolean
:group 'eshell-cmd)
:type 'boolean)
(defcustom eshell-lisp-regexp "\\([(`]\\|#'\\)"
"A regexp which, if matched at beginning of an argument, means Lisp.
Such arguments will be passed to `read', and then evaluated."
:type 'regexp
:group 'eshell-cmd)
:type 'regexp)
(defcustom eshell-pre-command-hook nil
"A hook run before each interactive command is invoked."
:type 'hook
:group 'eshell-cmd)
:type 'hook)
(defcustom eshell-post-command-hook nil
"A hook run after each interactive command is invoked."
:type 'hook
:group 'eshell-cmd)
:type 'hook)
(defcustom eshell-prepare-command-hook nil
"A set of functions called to prepare a named command.
......@@ -149,8 +145,7 @@ the value of these symbols if necessary.
To prevent a command from executing at all, set
`eshell-last-command-name' to nil."
:type 'hook
:group 'eshell-cmd)
:type 'hook)
(defcustom eshell-named-command-hook nil
"A set of functions called before a named command is invoked.
......@@ -165,7 +160,7 @@ In order to substitute an alternate command form for execution, the
hook function should throw it using the tag `eshell-replace-command'.
For example:
(add-hook \\='eshell-named-command-hook \\='subst-with-cd)
(add-hook \\='eshell-named-command-hook #\\='subst-with-cd)
(defun subst-with-cd (command args)
(throw \\='eshell-replace-command
(eshell-parse-command \"cd\" args)))
......@@ -173,8 +168,7 @@ For example:
Although useless, the above code will cause any non-glob, non-Lisp
command (i.e., `ls' as opposed to `*ls' or `(ls)') to be replaced by a
call to `cd' using the arguments that were passed to the function."
:type 'hook
:group 'eshell-cmd)
:type 'hook)
(defcustom eshell-pre-rewrite-command-hook
'(eshell-no-command-conversion
......@@ -182,8 +176,7 @@ call to `cd' using the arguments that were passed to the function."
"A hook run before command rewriting begins.
The terms of the command to be rewritten is passed as arguments, and
may be modified in place. Any return value is ignored."
:type 'hook
:group 'eshell-cmd)
:type 'hook)
(defcustom eshell-rewrite-command-hook
'(eshell-rewrite-for-command
......@@ -202,8 +195,7 @@ so by adding a function to this hook. The first function to return a
substitute command form is the one used. Each function is passed the
command's full argument list, which is a list of sexps (typically
forms or strings)."
:type 'hook
:group 'eshell-cmd)
:type 'hook)
(defvar eshell-post-rewrite-command-function #'identity
"Function run after command rewriting is finished.
......@@ -228,16 +220,14 @@ If an entry is a function, it will be called with the name, and should
return non-nil if the command is complex."
:type '(repeat :tag "Commands"
(choice (string :tag "Name")
(function :tag "Predicate")))
:group 'eshell-cmd)
(function :tag "Predicate"))))
;;; User Variables:
(defcustom eshell-cmd-load-hook nil
"A hook that gets run when `eshell-cmd' is loaded."
:version "24.1" ; removed eshell-cmd-initialize
:type 'hook
:group 'eshell-cmd)
:type 'hook)
(defcustom eshell-debug-command nil
"If non-nil, enable Eshell debugging code.
......@@ -247,9 +237,8 @@ you must re-load `esh-cmd.el'."
:initialize 'custom-initialize-default
:set (lambda (symbol value)
(set symbol value)
(load-library "esh-cmd"))
:type 'boolean
:group 'eshell-cmd)
(load "esh-cmd"))
:type 'boolean)
(defcustom eshell-deferrable-commands
'(eshell-named-command
......@@ -259,16 +248,14 @@ you must re-load `esh-cmd.el'."
If they return a process object, execution of the calling Eshell
command will wait for completion (in the background) before finishing
the command."
:type '(repeat function)
:group 'eshell-cmd)
:type '(repeat function))
(defcustom eshell-subcommand-bindings
'((eshell-in-subcommand-p t)
(default-directory default-directory)
(process-environment (eshell-copy-environment)))
"A list of `let' bindings for subcommand environments."
:type 'sexp
:group 'eshell-cmd)
:type 'sexp)
(put 'risky-local-variable 'eshell-subcommand-bindings t)
......@@ -307,7 +294,7 @@ otherwise t.")
(set (make-local-variable 'eshell-last-command-name) nil)
(set (make-local-variable 'eshell-last-async-proc) nil)
(add-hook 'eshell-kill-hook 'eshell-resume-command nil t)
(add-hook 'eshell-kill-hook #'eshell-resume-command nil t)
;; make sure that if a command is over, and no process is being
;; waited for, that `eshell-current-command' is set to nil. This
......@@ -317,16 +304,17 @@ otherwise t.")
(function
(lambda ()
(setq eshell-current-command nil
eshell-last-async-proc nil))) nil t)
eshell-last-async-proc nil)))
nil t)
(add-hook 'eshell-parse-argument-hook
'eshell-parse-subcommand-argument nil t)
#'eshell-parse-subcommand-argument nil t)
(add-hook 'eshell-parse-argument-hook
'eshell-parse-lisp-argument nil t)
#'eshell-parse-lisp-argument nil t)
(when (eshell-using-module 'eshell-cmpl)
(add-hook 'pcomplete-try-first-hook
'eshell-complete-lisp-symbols nil t)))
#'eshell-complete-lisp-symbols nil t)))
(defun eshell-complete-lisp-symbols ()
"If there is a user reference, complete it."
......@@ -724,6 +712,8 @@ ensconced in a list."
eshell-current-subjob-p)
,object))
(defvar eshell-this-command-hook nil)
(defmacro eshell-trap-errors (object)
"Trap any errors that occur, so they are not entirely fatal.
Also, the variable `eshell-this-command-hook' is available for the
......@@ -736,9 +726,9 @@ this grossness will be made to disappear by using `call/cc'..."
(eshell-condition-case err
(prog1
,object
(run-hooks 'eshell-this-command-hook))
(mapc #'funcall eshell-this-command-hook))
(error
(run-hooks 'eshell-this-command-hook)
(mapc #'funcall eshell-this-command-hook)
(eshell-errorn (error-message-string err))
(eshell-close-handles 1)))))
......@@ -1059,16 +1049,8 @@ be finished later after the completion of an asynchronous subprocess."
((eq (car form) 'setcdr)
(setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p))
(eval form))
((memq (car form) '(let catch condition-case unwind-protect))
;; `let', `condition-case' and `unwind-protect' have to be
;; handled specially, because we only want to call
;; `eshell-do-eval' on their first form.
;;
;; NOTE: This requires obedience by all forms which this
;; function might encounter, that they do not contain
;; other special forms.
(if (and (eq (car form) 'let)
(not (eq (car (cadr args)) 'eshell-do-eval)))
((eq (car form) 'let)
(if (not (eq (car (cadr args)) 'eshell-do-eval))
(eshell-manipulate "evaluating let args"
(dolist (letarg (car args))
(if (and (listp letarg)
......@@ -1076,6 +1058,21 @@ be finished later after the completion of an asynchronous subprocess."
(setcdr letarg
(list (eshell-do-eval
(cadr letarg) synchronous-p)))))))
(cl-progv
(mapcar (lambda (binding) (if (consp binding) (car binding) binding))
(car args))
;; These expressions should all be constants now.
(mapcar (lambda (binding) (if (consp binding) (eval (cadr binding))))
(car args))
(eshell-do-eval (macroexp-progn (cdr args)) synchronous-p)))
((memq (car form) '(catch condition-case unwind-protect))
;; `condition-case' and `unwind-protect' have to be
;; handled specially, because we only want to call
;; `eshell-do-eval' on their first form.
;;
;; NOTE: This requires obedience by all forms which this
;; function might encounter, that they do not contain
;; other special forms.
(unless (eq (car form) 'unwind-protect)
(setq args (cdr args)))
(unless (eq (caar args) 'eshell-do-eval)
......@@ -1158,10 +1155,9 @@ be finished later after the completion of an asynchronous subprocess."
(setq name (substring name 1)
direct t))
(if (and (not direct)
(eshell-using-module 'eshell-alias)
(fboundp 'eshell-lookup-alias)
(setq alias
(funcall (symbol-function 'eshell-lookup-alias)
name)))
(eshell-lookup-alias name)))
(setq program
(concat name " is an alias, defined as \""
(cadr alias) "\"")))
......
......@@ -128,60 +128,55 @@ variable value, a subcommand, or even the result of a Lisp form."
(defcustom eshell-var-load-hook nil
"A list of functions to call when loading `eshell-var'."
:version "24.1" ; removed eshell-var-initialize
:type 'hook
:group 'eshell-var)
:type 'hook)
(defcustom eshell-prefer-lisp-variables nil
"If non-nil, prefer Lisp variables to environment variables."
:type 'boolean
:group 'eshell-var)
:type 'boolean)
(defcustom eshell-complete-export-definition t
"If non-nil, completing names for `export' shows current definition."
:type 'boolean
:group 'eshell-var)
:type 'boolean)
(defcustom eshell-modify-global-environment nil
"If non-nil, using `export' changes Emacs's global environment."
:type 'boolean
:group 'eshell-var)
:type 'boolean)
(defcustom eshell-variable-name-regexp "[A-Za-z0-9_-]+"
"A regexp identifying what constitutes a variable name reference.
Note that this only applies for `$NAME'. If the syntax `$<NAME>' is
used, then NAME can contain any character, including angle brackets,
if they are quoted with a backslash."
:type 'regexp
:group 'eshell-var)
:type 'regexp)
(defcustom eshell-variable-aliases-list
'(;; for eshell.el
("COLUMNS" (lambda (indices) (window-width)) t)
("LINES" (lambda (indices) (window-height)) t)
`(;; for eshell.el
("COLUMNS" ,(lambda (_indices) (window-width)) t)
("LINES" ,(lambda (_indices) (window-height)) t)
;; for eshell-cmd.el
("_" (lambda (indices)
(if (not indices)
(car (last eshell-last-arguments))
(eshell-apply-indices eshell-last-arguments
indices))))
("_" ,(lambda (indices)
(if (not indices)
(car (last eshell-last-arguments))
(eshell-apply-indices eshell-last-arguments
indices))))
("?" eshell-last-command-status)
("$" eshell-last-command-result)
("0" eshell-command-name)
("1" (lambda (indices) (nth 0 eshell-command-arguments)))
("2" (lambda (indices) (nth 1 eshell-command-arguments)))
("3" (lambda (indices) (nth 2 eshell-command-arguments)))
("4" (lambda (indices) (nth 3 eshell-command-arguments)))
("5" (lambda (indices) (nth 4 eshell-command-arguments)))
("6" (lambda (indices) (nth 5 eshell-command-arguments)))
("7" (lambda (indices) (nth 6 eshell-command-arguments)))
("8" (lambda (indices) (nth 7 eshell-command-arguments)))
("9" (lambda (indices) (nth 8 eshell-command-arguments)))
("*" (lambda (indices)
(if (not indices)
eshell-command-arguments
(eshell-apply-indices eshell-command-arguments
indices)))))
("1" ,(lambda (_indices) (nth 0 eshell-command-arguments)))
("2" ,(lambda (_indices) (nth 1 eshell-command-arguments)))
("3" ,(lambda (_indices) (nth 2 eshell-command-arguments)))
("4" ,(lambda (_indices) (nth 3 eshell-command-arguments)))
("5" ,(lambda (_indices) (nth 4 eshell-command-arguments)))
("6" ,(lambda (_indices) (nth 5 eshell-command-arguments)))
("7" ,(lambda (_indices) (nth 6 eshell-command-arguments)))
("8" ,(lambda (_indices) (nth 7 eshell-command-arguments)))
("9" ,(lambda (_indices) (nth 8 eshell-command-arguments)))
("*" ,(lambda (indices)
(if (not indices)
eshell-command-arguments
(eshell-apply-indices eshell-command-arguments
indices)))))
"This list provides aliasing for variable references.
It is very similar in concept to what `eshell-user-aliases-list' does
for commands. Each member of this defines the name of a command,
......@@ -197,8 +192,7 @@ function), and the arguments passed to this function would be the list
'(10 20)', and nil."
:type '(repeat (list string sexp
(choice (const :tag "Copy to environment" t)
(const :tag "Use only in Eshell" nil))))
:group 'eshell-var)
(const :tag "Use only in Eshell" nil)))))
(put 'eshell-variable-aliases-list 'risky-local-variable t)
......@@ -397,6 +391,8 @@ process any indices that come after the variable reference."
indices (and (not (eobp))
(eq (char-after) ?\[)
(eshell-parse-indices))
;; This is an expression that will be evaluated by `eshell-do-eval',
;; which only support let-binding of dynamically-scoped vars
value `(let ((indices ',indices)) ,value))
(if get-len
`(length ,value)
......@@ -419,18 +415,17 @@ Possible options are:
(if (not end)
(throw 'eshell-incomplete ?\{)
(prog1
(list 'eshell-convert
(list 'eshell-command-to-value
(list 'eshell-as-subcommand
(eshell-parse-command
(cons (1+ (point)) end)))))
`(eshell-convert
(eshell-command-to-value
(eshell-as-subcommand
,(eshell-parse-command (cons (1+ (point)) end)))))
(goto-char (1+ end))))))
((memq (char-after) '(?\' ?\"))
(let ((name (if (eq (char-after) ?\')
(eshell-parse-literal-quote)
(eshell-parse-double-quote))))
(if name
(list 'eshell-get-variable (eval name) 'indices))))
`(eshell-get-variable ,(eval name) indices))))
((eq (char-after) ?\<)
(let ((end (eshell-find-delimiter ?\< ?\>)))
(if (not end)
......@@ -439,37 +434,30 @@ Possible options are:
(cmd (concat (buffer-substring (1+ (point)) end)
" > " temp)))
(prog1
(list
'let (list (list 'eshell-current-handles
(list 'eshell-create-handles temp
(list 'quote 'overwrite))))
(list
'progn
(list 'eshell-as-subcommand
(eshell-parse-command cmd))
(list 'ignore
(list 'nconc 'eshell-this-command-hook
(list 'list
(list 'function
(list 'lambda nil
(list 'delete-file temp))))))
(list 'quote temp)))
`(let ((eshell-current-handles
(eshell-create-handles ,temp 'overwrite)))
(progn
(eshell-as-subcommand ,(eshell-parse-command cmd))
(ignore
(nconc eshell-this-command-hook
(list (function (lambda ()
(delete-file ,temp))))))
(quote ,temp)))
(goto-char (1+ end)))))))
((eq (char-after) ?\()
(condition-case nil
(list 'eshell-command-to-value
(list 'eshell-lisp-command
(list 'quote (read (current-buffer)))))
`(eshell-command-to-value
(eshell-lisp-command
',(read (current-buffer))))
(end-of-file
(throw 'eshell-incomplete ?\())))
((assoc (char-to-string (char-after))
eshell-variable-aliases-list)
(forward-char)
(list 'eshell-get-variable
(char-to-string (char-before)) 'indices))
`(eshell-get-variable ,(char-to-string (char-before)) indices))
((looking-at eshell-variable-name-regexp)
(prog1
(list 'eshell-get-variable (match-string 0) 'indices)
`(eshell-get-variable ,(match-string 0) indices)
(goto-char (match-end 0))))
(t
(error "Invalid variable reference"))))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment