mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2025-01-07 15:21:46 +00:00
Merge remote-tracking branch 'origin/master' into feature/android
This commit is contained in:
commit
2baf2c5fd9
@ -3,10 +3,10 @@ NOTES ON TREESIT_RECORD_CHANGE
|
||||
It is vital that Emacs informs tree-sitter of every change made to the
|
||||
buffer, lest tree-sitter's parse tree would be corrupted/out of sync.
|
||||
|
||||
All buffer changes in Emacs are made through functions in insdel.c
|
||||
(and casefiddle.c), I augmented functions in those files with calls to
|
||||
treesit_record_change. Below is a manifest of all the relevant
|
||||
functions in insdel.c as of Emacs 29:
|
||||
Almost all buffer changes in Emacs are made through functions in
|
||||
insdel.c (see below for exceptions), I augmented functions in insdel.c
|
||||
with calls to treesit_record_change. Below is a manifest of all the
|
||||
relevant functions in insdel.c as of Emacs 29:
|
||||
|
||||
Function Calls
|
||||
----------------------------------------------------------------------
|
||||
@ -43,8 +43,176 @@ insert_from_buffer but not insert_from_buffer_1. I also left a
|
||||
reminder comment.
|
||||
|
||||
|
||||
As for casefiddle.c, do_casify_unibyte_region and
|
||||
EXCEPTIONS
|
||||
|
||||
|
||||
There are a couple of functions that replaces characters in-place
|
||||
rather than insert/delete. They are in casefiddle.c and editfns.c.
|
||||
|
||||
In casefiddle.c, do_casify_unibyte_region and
|
||||
do_casify_multibyte_region modifies buffer, but they are static
|
||||
functions and are called by casify_region, which calls
|
||||
treesit_record_change. Other higher-level functions calls
|
||||
casify_region to do the work.
|
||||
casify_region to do the work.
|
||||
|
||||
In editfns.c, subst-char-in-region and translate-region-internal might
|
||||
replace characters in-place, I made them to call
|
||||
treesit_record_change. transpose-regions uses memcpy to move text
|
||||
around, it calls treesit_record_change too.
|
||||
|
||||
I found these exceptions by grepping for signal_after_change and
|
||||
checking each caller manually. Below is all the result as of Emacs 29
|
||||
and some comment for each one. Readers can use
|
||||
|
||||
(highlight-regexp "^[^[:space:]]+?\\.c:[[:digit:]]+:[^z-a]+?$" 'highlight)
|
||||
|
||||
to make things easier to read.
|
||||
|
||||
grep [...] --color=auto -i --directories=skip -nH --null -e signal_after_change *.c
|
||||
|
||||
callproc.c:789: calling prepare_to_modify_buffer and signal_after_change.
|
||||
callproc.c:793: is one call to signal_after_change in each of the
|
||||
callproc.c:800: signal_after_change hasn't. A continue statement
|
||||
callproc.c:804: again, and this time signal_after_change gets called,
|
||||
|
||||
Not code.
|
||||
|
||||
callproc.c:820: signal_after_change (PT - nread, 0, nread);
|
||||
callproc.c:863: signal_after_change (PT - process_coding.produced_char,
|
||||
|
||||
Both are called in call-process. I don’t think we’ll ever use
|
||||
tree-sitter in call-process’s stdio buffer, right? I didn’t check
|
||||
line-by-line, but it seems to only use insert_1_both and del_range_2.
|
||||
|
||||
casefiddle.c:558: signal_after_change (start, end - start - added, end - start);
|
||||
|
||||
Called in casify-region, calls treesit_record_change.
|
||||
|
||||
decompress.c:195: signal_after_change (data->orig, data->start - data->orig,
|
||||
|
||||
Called in unwind_decompress, uses del_range_2, insdel function.
|
||||
|
||||
decompress.c:334: signal_after_change (istart, iend - istart, unwind_data.nbytes);
|
||||
|
||||
Called in zlib-decompress-region, uses del_range_2, insdel function.
|
||||
|
||||
editfns.c:2139: signal_after_change (BEGV, size_a, ZV - BEGV);
|
||||
|
||||
Called in replace-buffer-contents, which calls del_range and
|
||||
Finsert_buffer_substring, both are ok.
|
||||
|
||||
editfns.c:2416: signal_after_change (changed,
|
||||
|
||||
Called in subst-char-in-region, which either calls replace_range (a
|
||||
insdel function) or modifies buffer content by itself (need to call
|
||||
treesit_record_change).
|
||||
|
||||
editfns.c:2544: /* Reload as signal_after_change in last iteration may GC. */
|
||||
|
||||
Not code.
|
||||
|
||||
editfns.c:2604: signal_after_change (pos, 1, 1);
|
||||
|
||||
Called in translate-region-internal, which has three cases:
|
||||
|
||||
if (nc != oc && nc >= 0) {
|
||||
if (len != str_len) {
|
||||
replace_range()
|
||||
} else {
|
||||
while (str_len-- > 0)
|
||||
*p++ = *str++;
|
||||
}
|
||||
}
|
||||
else if (nc < 0) {
|
||||
replace_range()
|
||||
}
|
||||
|
||||
replace_range is ok, but in the case where it manually modifies buffer
|
||||
content, it needs to call treesit_record_change.
|
||||
|
||||
editfns.c:4779: signal_after_change (start1, end2 - start1, end2 - start1);
|
||||
|
||||
Called in transpose-regions. It just uses memcpy’s and doesn’t use
|
||||
insdel functions; needs to call treesit_record_change.
|
||||
|
||||
fileio.c:4825: signal_after_change (PT, 0, inserted);
|
||||
|
||||
Called in insert_file_contents. Uses insert_1_both (very first in the
|
||||
function); del_range_1 and del_range_byte (the optimized way to
|
||||
implement replace when decoding isn’t needed); del_range_byte and
|
||||
insert_from_buffer (the optimized way used when decoding is needed);
|
||||
decode_coding_gap or insert_from_gap_1 (I’m not sure the condition for
|
||||
this, but anyway it’s safe). The function also calls memcpy and
|
||||
memmove, but they are irrelevant: memcpy is used for decoding, and
|
||||
memmove is moving stuff inside the gap for decode_coding_gap.
|
||||
|
||||
I’d love someone to verify this function, since it’s so complicated
|
||||
and large, but from what I can tell it’s safe.
|
||||
|
||||
fns.c:3998: signal_after_change (XFIXNAT (beg), 0, inserted_chars);
|
||||
|
||||
Called in base64-decode-region, uses insert_1_both and del_range_both,
|
||||
safe.
|
||||
|
||||
insdel.c:681: signal_after_change (opoint, 0, len);
|
||||
insdel.c:696: signal_after_change (opoint, 0, len);
|
||||
insdel.c:741: signal_after_change (opoint, 0, len);
|
||||
insdel.c:757: signal_after_change (opoint, 0, len);
|
||||
insdel.c:976: signal_after_change (opoint, 0, PT - opoint);
|
||||
insdel.c:996: signal_after_change (opoint, 0, PT - opoint);
|
||||
insdel.c:1187: signal_after_change (opoint, 0, PT - opoint);
|
||||
insdel.c:1412: signal_after_change. */
|
||||
insdel.c:1585: signal_after_change (from, nchars_del, GPT - from);
|
||||
insdel.c:1600: prepare_to_modify_buffer and never call signal_after_change.
|
||||
insdel.c:1603: region once. Apart from signal_after_change, any caller of this
|
||||
insdel.c:1747: signal_after_change (from, to - from, 0);
|
||||
insdel.c:1789: signal_after_change (from, to - from, 0);
|
||||
insdel.c:1833: signal_after_change (from, to - from, 0);
|
||||
insdel.c:2223:signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
|
||||
insdel.c:2396: signal_after_change (begpos, endpos - begpos - change, endpos - begpos);
|
||||
|
||||
I’ve checked all insdel functions. We can assume insdel functions are
|
||||
all safe.
|
||||
|
||||
json.c:790: signal_after_change (PT, 0, inserted);
|
||||
|
||||
Called in json-insert, calls either decode_coding_gap or
|
||||
insert_from_gap_1, both are safe. Calls memmove but it’s for
|
||||
decode_coding_gap.
|
||||
|
||||
keymap.c:2873: /* Insert calls signal_after_change which may GC. */
|
||||
|
||||
Not code.
|
||||
|
||||
print.c:219: signal_after_change (PT - print_buffer.pos, 0, print_buffer.pos);
|
||||
|
||||
Called in print_finish, calls copy_text and insert_1_both, safe.
|
||||
|
||||
process.c:6365: process buffer is changed in the signal_after_change above.
|
||||
search.c:2763: (see signal_before_change and signal_after_change). Try to error
|
||||
|
||||
Not code.
|
||||
|
||||
search.c:2777: signal_after_change (sub_start, sub_end - sub_start, SCHARS (newtext));
|
||||
|
||||
Called in replace_match. Calls replace_range, upcase-region,
|
||||
upcase-initials-region (both calls casify_region in the end), safe.
|
||||
Calls memcpy but it’s for string manipulation.
|
||||
|
||||
textprop.c:1261: signal_after_change (XFIXNUM (start), XFIXNUM (end) - XFIXNUM (start),
|
||||
textprop.c:1272: signal_after_change (XFIXNUM (start), XFIXNUM (end) - XFIXNUM (start),
|
||||
textprop.c:1283: signal_after_change (XFIXNUM (start), XFIXNUM (end) - XFIXNUM (start),
|
||||
textprop.c:1458: signal_after_change (XFIXNUM (start), XFIXNUM (end) - XFIXNUM (start),
|
||||
textprop.c:1652: signal_after_change (XFIXNUM (start), XFIXNUM (end) - XFIXNUM (start),
|
||||
textprop.c:1661: signal_after_change (XFIXNUM (start), XFIXNUM (end) - XFIXNUM (start),
|
||||
textprop.c:1672: signal_after_change (XFIXNUM (start), XFIXNUM (end) - XFIXNUM (start),
|
||||
textprop.c:1750: before changes are made and signal_after_change when we are done.
|
||||
textprop.c:1752: and call signal_after_change before returning if MODIFIED. */
|
||||
textprop.c:1764: signal_after_change (XFIXNUM (start),
|
||||
textprop.c:1778: signal_after_change (XFIXNUM (start), XFIXNUM (end) - XFIXNUM (start),
|
||||
textprop.c:1791: signal_after_change (XFIXNUM (start), XFIXNUM (end) - XFIXNUM (start),
|
||||
textprop.c:1810: signal_after_change (XFIXNUM (start),
|
||||
|
||||
We don’t care about text property changes.
|
||||
|
||||
Grep finished with 51 matches found at Wed Jun 28 15:12:23
|
||||
|
@ -351,7 +351,7 @@ Registers
|
||||
* Position Registers:: Saving positions in registers.
|
||||
* Text Registers:: Saving text in registers.
|
||||
* Rectangle Registers:: Saving rectangles in registers.
|
||||
* Configuration Registers:: Saving window configurations in registers.
|
||||
* Configuration Registers:: Saving window/frame configurations in registers.
|
||||
* Number Registers:: Numbers in registers.
|
||||
* File and Buffer Registers:: File and buffer names in registers.
|
||||
* Keyboard Macro Registers:: Keyboard macros in registers.
|
||||
|
@ -716,7 +716,7 @@ started editing (@pxref{Old Revisions}), type @kbd{C-c C-d}
|
||||
(@code{log-edit-generate-changelog-from-diff}), to generate skeleton
|
||||
ChangeLog entries, listing all changed file and function names based
|
||||
on the diff of the VC fileset. Consecutive entries left empty will be
|
||||
combined by @kbd{C-q} (@code{fill-paragraph}). By default the
|
||||
combined by @kbd{M-q} (@code{fill-paragraph}). By default the
|
||||
skeleton will just include the file name, without any leading
|
||||
directories. If you wish to prepend the leading directories up to the
|
||||
VC root, customize @code{diff-add-log-use-relative-names}.
|
||||
|
@ -625,7 +625,7 @@ checkout instead of cloning a remote repository. You can do this by
|
||||
using @code{package-vc-install-from-checkout}, which creates a symbolic link
|
||||
from the package directory (@pxref{Package Files}) to your checkout
|
||||
and initializes the code. Note that you might have to use
|
||||
@code{package-vc-refresh} to repeat the initialization and update the
|
||||
@code{package-vc-rebuild} to repeat the initialization and update the
|
||||
autoloads.
|
||||
|
||||
@subsection Specifying Package Sources
|
||||
|
@ -22,10 +22,11 @@ because these keys are reserved for quitting (@pxref{Quitting}).
|
||||
|
||||
@findex view-register
|
||||
A register can store a position, a piece of text, a rectangle, a
|
||||
number, a window configuration, or a file name, but only one thing at
|
||||
any given time. Whatever you store in a register remains there until
|
||||
you store something else in that register. To see what register
|
||||
@var{r} contains, use @kbd{M-x view-register}:
|
||||
number, a window or frame configuration, a buffer name, or a file
|
||||
name, but only one thing at any given time. Whatever you store in a
|
||||
register remains there until you store something else in that
|
||||
register. To see what register @var{r} contains, use @kbd{M-x
|
||||
view-register}:
|
||||
|
||||
@table @kbd
|
||||
@item M-x view-register @key{RET} @var{r}
|
||||
@ -50,7 +51,7 @@ this chapter.
|
||||
* Position Registers:: Saving positions in registers.
|
||||
* Text Registers:: Saving text in registers.
|
||||
* Rectangle Registers:: Saving rectangles in registers.
|
||||
* Configuration Registers:: Saving window configurations in registers.
|
||||
* Configuration Registers:: Saving window/frame configurations in registers.
|
||||
* Number Registers:: Numbers in registers.
|
||||
* File and Buffer Registers:: File and buffer names in registers.
|
||||
* Keyboard Macro Registers:: Keyboard macros in registers.
|
||||
@ -182,8 +183,10 @@ previously documented in @ref{Text Registers}, inserts a rectangle
|
||||
rather than a text string, if the register contains a rectangle.
|
||||
|
||||
@node Configuration Registers
|
||||
@section Saving Window Configurations in Registers
|
||||
@section Saving Window and Frame Configurations in Registers
|
||||
@cindex saving window configuration in a register
|
||||
@cindex saving frame configuration in a register
|
||||
@cindex frameset, saving in a register
|
||||
|
||||
@findex window-configuration-to-register
|
||||
@findex frameset-to-register
|
||||
@ -191,16 +194,17 @@ rather than a text string, if the register contains a rectangle.
|
||||
@kindex C-x r f
|
||||
You can save the window configuration of the selected frame in a
|
||||
register, or even the configuration of all windows in all frames, and
|
||||
restore the configuration later. @xref{Windows}, for information
|
||||
about window configurations.
|
||||
restore the configuration later. @xref{Window Convenience}, for
|
||||
information about window configurations.
|
||||
|
||||
@table @kbd
|
||||
@item C-x r w @var{r}
|
||||
Save the state of the selected frame's windows in register @var{r}
|
||||
(@code{window-configuration-to-register}).
|
||||
@cindex frameset
|
||||
@item C-x r f @var{r}
|
||||
Save the state of all frames, including all their windows, in register
|
||||
@var{r} (@code{frameset-to-register}).
|
||||
Save the state of all frames, including all their windows (a.k.a.@:
|
||||
@dfn{frameset}), in register @var{r} (@code{frameset-to-register}).
|
||||
@end table
|
||||
|
||||
Use @kbd{C-x r j @var{r}} to restore a window or frame configuration.
|
||||
@ -266,7 +270,7 @@ puts the file name shown in register @samp{z}.
|
||||
@var{r}}. (This is the same command used to jump to a position or
|
||||
restore a frame configuration.)
|
||||
|
||||
Similarly, if there's certain buffers you visit frequently, you
|
||||
Similarly, if there are certain buffers you visit frequently, you
|
||||
can put their names in registers. For instance, if you visit the
|
||||
@samp{*Messages*} buffer often, you can use the following snippet to
|
||||
put that buffer into the @samp{m} register:
|
||||
@ -275,6 +279,9 @@ put that buffer into the @samp{m} register:
|
||||
(set-register ?m '(buffer . "*Messages*"))
|
||||
@end smallexample
|
||||
|
||||
To switch to the buffer whose name is in register @var{r}, type
|
||||
@kbd{C-x r j @var{r}}.
|
||||
|
||||
@node Keyboard Macro Registers
|
||||
@section Keyboard Macro Registers
|
||||
@cindex saving keyboard macro in a register
|
||||
|
@ -1969,7 +1969,7 @@ ts_node_field_name_for_child treesit-node-field-name-for-child
|
||||
ts_node_child_count treesit-node-child-count
|
||||
ts_node_named_child treesit-node-child
|
||||
ts_node_named_child_count treesit-node-child-count
|
||||
ts_node_child_by_field_name treesit-node-by-field-name
|
||||
ts_node_child_by_field_name treesit-node-child-by-field-name
|
||||
ts_node_child_by_field_id
|
||||
ts_node_next_sibling treesit-node-next-sibling
|
||||
ts_node_prev_sibling treesit-node-prev-sibling
|
||||
@ -1977,9 +1977,9 @@ ts_node_next_named_sibling treesit-node-next-sibling
|
||||
ts_node_prev_named_sibling treesit-node-prev-sibling
|
||||
ts_node_first_child_for_byte treesit-node-first-child-for-pos
|
||||
ts_node_first_named_child_for_byte treesit-node-first-child-for-pos
|
||||
ts_node_descendant_for_byte_range treesit-descendant-for-range
|
||||
ts_node_descendant_for_byte_range treesit-node-descendant-for-range
|
||||
ts_node_descendant_for_point_range
|
||||
ts_node_named_descendant_for_byte_range treesit-descendant-for-range
|
||||
ts_node_named_descendant_for_byte_range treesit-node-descendant-for-range
|
||||
ts_node_named_descendant_for_point_range
|
||||
ts_node_edit
|
||||
ts_node_eq treesit-node-eq
|
||||
|
@ -3398,37 +3398,43 @@ for @var{object} is the current buffer.
|
||||
@end defun
|
||||
|
||||
@defun text-property-search-forward prop &optional value predicate not-current
|
||||
Search for the next region that has text property @var{prop} set to
|
||||
@var{value} according to @var{predicate}.
|
||||
Search for the next region of text whose property @var{prop} is a
|
||||
match for @var{value} (which defaults to @code{nil}), according to
|
||||
@var{predicate}.
|
||||
|
||||
This function is modeled after @code{search-forward} and friends in
|
||||
that it moves point, but it returns a structure that describes the
|
||||
match instead of returning it in @code{match-beginning} and friends.
|
||||
This function is modeled after @code{search-forward} (@pxref{String
|
||||
Search}) and friends, in that it moves point, but it also returns a
|
||||
structure that describes the match instead of returning it in
|
||||
@code{match-beginning} and friends.
|
||||
|
||||
If the text property can't be found, the function returns @code{nil}.
|
||||
If it's found, point is placed at the end of the region that has this
|
||||
text property match, and a @code{prop-match} structure is returned.
|
||||
If the text property whose value is a match can't be found, the
|
||||
function returns @code{nil}. If it's found, point is placed at the
|
||||
end of the region that has this matching text property, and the
|
||||
function returns a @code{prop-match} structure with information about
|
||||
the match.
|
||||
|
||||
@var{predicate} can either be @code{t} (which is a synonym for
|
||||
@code{equal}), @code{nil} (which means ``not equal''), or a predicate
|
||||
that will be called with two parameters: The first is @var{value}, and
|
||||
the second is the value of the text property we're inspecting.
|
||||
that will be called with two arguments: @var{value} and the value of
|
||||
the text property @var{prop} at the buffer position that is a
|
||||
candidate for a match. The function should return non-@code{nil} if
|
||||
there's a match, @code{nil} otherwise.
|
||||
|
||||
If @var{not-current}, if point is in a region where we have a match,
|
||||
then skip past that and find the next instance instead.
|
||||
If @var{not-current} is non-@code{nil}, then if point is already in a
|
||||
region where we have a property match, skip past that region and find
|
||||
the next region instead.
|
||||
|
||||
The @code{prop-match} structure has the following accessors:
|
||||
The @code{prop-match} structure has the following accessor functionss:
|
||||
@code{prop-match-beginning} (the start of the match),
|
||||
@code{prop-match-end} (the end of the match), and
|
||||
@code{prop-match-value} (the value of @var{property} at the start of
|
||||
the match).
|
||||
|
||||
In the examples below, imagine that you're in a buffer that looks like
|
||||
this:
|
||||
In the examples below, we use a buffer whose contents is:
|
||||
|
||||
@example
|
||||
This is a bold and here's bolditalic and this is the end.
|
||||
@end example
|
||||
@display
|
||||
This is a @b{bold} and here's @b{@i{bolditalic}} and this is the end.
|
||||
@end display
|
||||
|
||||
That is, the ``bold'' words are the @code{bold} face, and the
|
||||
``italic'' word is in the @code{italic} face.
|
||||
@ -3452,8 +3458,9 @@ This will pick out all the words that use the @code{bold} face.
|
||||
@end lisp
|
||||
|
||||
This will pick out all the bits that have no face properties, which
|
||||
will result in the list @samp{("This is a " "and here's " "and this is
|
||||
the end")} (only reversed, since we used @code{push}).
|
||||
will result in the list @samp{(@w{"This is a "} @w{"and here's "}
|
||||
@w{"and this is the end"})} (only in reverse order, since we used
|
||||
@code{push}, @pxref{List Variables}).
|
||||
|
||||
@lisp
|
||||
(while (setq match (text-property-search-forward 'face nil nil))
|
||||
@ -3481,8 +3488,8 @@ This will give you a list of all those URLs.
|
||||
|
||||
@defun text-property-search-backward prop &optional value predicate not-current
|
||||
This is just like @code{text-property-search-forward}, but searches
|
||||
backward instead. Point is placed at the beginning of the matched
|
||||
region instead of the end, though.
|
||||
backward instead, and if a match is found, point is placed at the
|
||||
beginning of the matched region instead of the end.
|
||||
@end defun
|
||||
|
||||
|
||||
@ -4642,20 +4649,25 @@ A rectangle is represented by a list of strings.
|
||||
This represents a window configuration to restore in one frame, and a
|
||||
position to jump to in the current buffer.
|
||||
|
||||
@c FIXME: Mention frameset here.
|
||||
@cindex frameset
|
||||
@item @code{(@var{frame-configuration} @var{position})}
|
||||
This represents a frame configuration to restore, and a position
|
||||
to jump to in the current buffer.
|
||||
to jump to in the current buffer. Frame configurations are also
|
||||
known as @dfn{framesets}.
|
||||
|
||||
@item (file @var{filename})
|
||||
@item @code{(file @var{filename})}
|
||||
This represents a file to visit; jumping to this value visits file
|
||||
@var{filename}.
|
||||
|
||||
@item (file-query @var{filename} @var{position})
|
||||
@item @code{(file-query @var{filename} @var{position})}
|
||||
This represents a file to visit and a position in it; jumping to this
|
||||
value visits file @var{filename} and goes to buffer position
|
||||
@var{position}. Restoring this type of position asks the user for
|
||||
confirmation first.
|
||||
|
||||
@item @code{(buffer @var{buffer-name})}
|
||||
This represents a buffer; jumping to this value switches to buffer
|
||||
@var{buffer-name}.
|
||||
@end table
|
||||
|
||||
The functions in this section return unpredictable values unless
|
||||
|
@ -350,6 +350,9 @@ variables.
|
||||
A function call is in the tail position if it's the very last thing
|
||||
done so that the value returned by the call is the value of @var{body}
|
||||
itself, as is the case in the recursive call to @code{sum} above.
|
||||
|
||||
@strong{Warning:} @code{named-let} works as expected only when
|
||||
lexical-binding is enabled. @xref{Lexical Binding}.
|
||||
@end defspec
|
||||
|
||||
Here is a complete list of the other facilities that create local
|
||||
@ -2599,7 +2602,7 @@ can be either @code{get} or @code{set}.
|
||||
You can make two variables synonyms and declare one obsolete at the
|
||||
same time using the macro @code{define-obsolete-variable-alias}.
|
||||
|
||||
@defmac define-obsolete-variable-alias obsolete-name current-name &optional when docstring
|
||||
@defmac define-obsolete-variable-alias obsolete-name current-name when &optional docstring
|
||||
This macro marks the variable @var{obsolete-name} as obsolete and also
|
||||
makes it an alias for the variable @var{current-name}. It is
|
||||
equivalent to the following:
|
||||
|
@ -586,14 +586,6 @@ the buffer is merely buried instead.
|
||||
Set environment variables using input like Bash's @command{export}, as
|
||||
in @samp{export @var{var1}=@var{val1} @var{var2}=@var{val2} @dots{}}.
|
||||
|
||||
@item expr
|
||||
@cmindex expr
|
||||
An implementation of @command{expr} using the Calc package.
|
||||
@xref{Top,,, calc, The GNU Emacs Calculator}.
|
||||
|
||||
This command can be loaded as part of the eshell-xtra module, which is
|
||||
disabled by default.
|
||||
|
||||
@item grep
|
||||
@cmindex grep
|
||||
@itemx agrep
|
||||
@ -628,15 +620,6 @@ the external @command{info} command, but uses Emacs's internal Info
|
||||
reader.
|
||||
@xref{Misc Help, , , emacs, The GNU Emacs Manual}.
|
||||
|
||||
@item intersection
|
||||
@cmindex intersection
|
||||
A wrapper around the function @code{cl-intersection} (@pxref{Lists as
|
||||
Sets,,, cl, GNU Emacs Common Lisp Emulation}). This command
|
||||
can be used for comparing lists of strings.
|
||||
|
||||
This command can be loaded as part of the eshell-xtra module, which is
|
||||
disabled by default.
|
||||
|
||||
@item jobs
|
||||
@cmindex jobs
|
||||
List subprocesses of the Emacs process, if any, using the function
|
||||
@ -706,15 +689,6 @@ Manual}. Otherwise call the external @command{make} command.
|
||||
Display Man pages using the Emacs @code{man} command.
|
||||
@xref{Man Page, , , emacs, The GNU Emacs Manual}.
|
||||
|
||||
@item mismatch
|
||||
@cmindex mismatch
|
||||
A wrapper around the function @code{cl-mismatch} (@pxref{Searching
|
||||
Sequences,,, cl, GNU Emacs Common Lisp Emulation}). This command can
|
||||
be used for comparing lists of strings.
|
||||
|
||||
This command can be loaded as part of the eshell-xtra module, which is
|
||||
disabled by default.
|
||||
|
||||
@item mkdir
|
||||
@cmindex mkdir
|
||||
Make new directories.
|
||||
@ -771,24 +745,6 @@ is required.
|
||||
@cmindex rmdir
|
||||
Removes directories if they are empty.
|
||||
|
||||
@item set-difference
|
||||
@cmindex set-difference
|
||||
A wrapper around the function @code{cl-set-difference} (@pxref{Lists as
|
||||
Sets,,, cl, GNU Emacs Common Lisp Emulation}). This command
|
||||
can be used for comparing lists of strings.
|
||||
|
||||
This command can be loaded as part of the eshell-xtra module, which is
|
||||
disabled by default.
|
||||
|
||||
@item set-exclusive-or
|
||||
@cmindex set-exclusive-or
|
||||
A wrapper around the function @code{cl-set-exclusive-or} (@pxref{Lists
|
||||
as Sets,,, cl, GNU Emacs Common Lisp Emulation}). This command can be
|
||||
used for comparing lists of strings.
|
||||
|
||||
This command can be loaded as part of the eshell-xtra module, which is
|
||||
disabled by default.
|
||||
|
||||
@item set
|
||||
@cmindex set
|
||||
Set variable values, using the function @code{set} like a command
|
||||
@ -808,27 +764,6 @@ Source an Eshell file in a subshell environment. This is not to be
|
||||
confused with the command @command{.}, which sources a file in the
|
||||
current environment.
|
||||
|
||||
@item su
|
||||
@cmindex su
|
||||
@itemx sudo
|
||||
@cmindex sudo
|
||||
@itemx doas
|
||||
@cmindex doas
|
||||
Uses TRAMP's @command{su}, @command{sudo}, or @command{doas} method
|
||||
@pxref{Inline methods, , , tramp} to run a command via @command{su},
|
||||
@command{sudo}, or @command{doas}. These commands are in the
|
||||
eshell-tramp module, which is disabled by default.
|
||||
|
||||
|
||||
@item substitute
|
||||
@cmindex substitute
|
||||
A wrapper around the function @code{cl-substitute} (@pxref{Sequence
|
||||
Functions,,, cl, GNU Emacs Common Lisp Emulation}). This command can
|
||||
be used for comparing lists of strings.
|
||||
|
||||
This command can be loaded as part of the eshell-xtra module, which is
|
||||
disabled by default.
|
||||
|
||||
@item time
|
||||
@cmindex time
|
||||
Show the time elapsed during a command's execution.
|
||||
@ -838,15 +773,6 @@ Show the time elapsed during a command's execution.
|
||||
Set or view the default file permissions for newly created files and
|
||||
directories.
|
||||
|
||||
@item union
|
||||
@cmindex union
|
||||
A wrapper around the function @code{cl-union} (@pxref{Lists as Sets,,,
|
||||
cl, GNU Emacs Common Lisp Emulation}). This command can be used for
|
||||
comparing lists of strings.
|
||||
|
||||
This command can be loaded as part of the eshell-xtra module, which is
|
||||
disabled by default.
|
||||
|
||||
@item unset
|
||||
@cmindex unset
|
||||
Unset one or more variables. As with @command{set}, a variable name
|
||||
@ -2019,66 +1945,95 @@ at the end of the command are excluded. This allows input like this:
|
||||
Eshell provides a facility for defining extension modules so that they
|
||||
can be disabled and enabled without having to unload and reload them,
|
||||
and to provide a common parent Customize group for the
|
||||
modules.@footnote{ERC provides a similar module facility.} An Eshell
|
||||
module is defined the same as any other library but one requirement: the
|
||||
module must define a Customize@footnote{@xref{Customization, , ,
|
||||
elisp, The Emacs Lisp Reference Manual}.}
|
||||
group using @code{eshell-defgroup} (in place of @code{defgroup}) with
|
||||
@code{eshell-module} as the parent group.@footnote{If the module has
|
||||
no user-customizable options, then there is no need to define it as an
|
||||
Eshell module.} You also need to load the following as shown:
|
||||
|
||||
@example
|
||||
(eval-when-compile
|
||||
(require 'cl-lib)
|
||||
(require 'esh-mode)
|
||||
(require 'eshell))
|
||||
|
||||
(require 'esh-util)
|
||||
@end example
|
||||
modules.@footnote{ERC provides a similar module facility.}
|
||||
|
||||
@menu
|
||||
* Optional modules::
|
||||
* Writing a module::
|
||||
* Module testing::
|
||||
* Directory handling::
|
||||
* Key rebinding::
|
||||
* Smart scrolling::
|
||||
* Terminal emulation::
|
||||
* Electric forward slash::
|
||||
@end menu
|
||||
|
||||
@node Writing a module
|
||||
@section Writing a module
|
||||
@node Optional modules
|
||||
@section Optional modules
|
||||
|
||||
This section is not yet written.
|
||||
In addition to the various modules enabled by default (documented
|
||||
above), Eshell provides several other modules which are @emph{not}
|
||||
enabled by default. If you want to enable these, you can add them to
|
||||
@code{eshell-modules-list}.
|
||||
|
||||
@node Module testing
|
||||
@section Module testing
|
||||
|
||||
This section is not yet written.
|
||||
|
||||
@node Directory handling
|
||||
@section Directory handling
|
||||
|
||||
This section is not yet written.
|
||||
@menu
|
||||
* Key rebinding::
|
||||
* Smart scrolling::
|
||||
* Electric forward slash::
|
||||
* Tramp extensions::
|
||||
* Extra built-in commands::
|
||||
@end menu
|
||||
|
||||
@node Key rebinding
|
||||
@section Key rebinding
|
||||
@subsection Key rebinding
|
||||
|
||||
This section is not yet written.
|
||||
This module allows for special keybindings that only take effect
|
||||
while the point is in a region of input text. The default keybindings
|
||||
mimic the bindings used in other shells when the user is editing new
|
||||
input text. To enable this module, add @code{eshell-rebind} to
|
||||
@code{eshell-modules-list}.
|
||||
|
||||
For example, it binds @kbd{C-u} to kill the current input text and
|
||||
@kbd{C-w} to @code{backward-kill-word}. If the history module is
|
||||
enabled, it also binds @kbd{C-p} and @kbd{C-n} to move through the
|
||||
input history.
|
||||
|
||||
If @code{eshell-confine-point-to-input} is non-@code{nil}, this module
|
||||
prevents certain commands from causing the point to leave the input
|
||||
area, such as @code{backward-word}, @code{previous-line}, etc.
|
||||
|
||||
@node Smart scrolling
|
||||
@section Smart scrolling
|
||||
@subsection Smart scrolling
|
||||
|
||||
This section is not yet written.
|
||||
This module combines the facility of normal, modern shells with some
|
||||
of the edit/review concepts inherent in the design of Plan 9's 9term.
|
||||
To enable it, add @code{eshell-smart} to @code{eshell-modules-list}.
|
||||
|
||||
@node Terminal emulation
|
||||
@section Terminal emulation
|
||||
@itemize @bullet
|
||||
@item
|
||||
When you invoke a command, it is assumed that you want to read the
|
||||
output of that command.
|
||||
|
||||
This section is not yet written.
|
||||
@item
|
||||
If the output is not what you wanted, it is assumed that you will want
|
||||
to edit, and then resubmit a refined version of that command.
|
||||
|
||||
@item
|
||||
If the output is valid, pressing any self-inserting character key will
|
||||
jump to end of the buffer and insert that character, in order to begin
|
||||
entry of a new command.
|
||||
|
||||
@item
|
||||
If you show an intention to edit the previous command -- by moving
|
||||
around within it -- then the next self-inserting characters will
|
||||
insert *there*, instead of at the bottom of the buffer.
|
||||
|
||||
@item
|
||||
If you show an intention to review old commands, such as @kbd{M-p} or
|
||||
@kbd{M-r}, point will jump to the bottom of the buffer before invoking
|
||||
that command.
|
||||
|
||||
@item
|
||||
If none of the above has happened yet (i.e.@: your point is just
|
||||
sitting on the previous command), you can use @kbd{SPC} and
|
||||
@kbd{BACKSPACE} (or @kbd{Delete}) to page forward and backward
|
||||
@emph{through the output of the last command only}. It will constrain
|
||||
the movement of the point and window so that the maximum amount of
|
||||
output is always displayed at all times.
|
||||
|
||||
@item
|
||||
While output is being generated from a command, the window will be
|
||||
constantly reconfigured (until it would otherwise make no difference)
|
||||
in order to always show you the most output from the command possible.
|
||||
This happens if you change window sizes, scroll, etc.
|
||||
@end itemize
|
||||
|
||||
@node Electric forward slash
|
||||
@section Electric forward slash
|
||||
@subsection Electric forward slash
|
||||
|
||||
To help with supplying absolute file name arguments to remote
|
||||
commands, you can add the @code{eshell-elecslash} module to
|
||||
@ -2132,6 +2087,104 @@ when chaining commands with the operators @code{&&}, @code{||},
|
||||
@code{|} and @code{;}, the electric forward slash is active only
|
||||
within the first command.
|
||||
|
||||
@node Tramp extensions
|
||||
@subsection Tramp extensions
|
||||
|
||||
This module adds built-in commands that use Tramp to handle running
|
||||
other commands as different users, replacing the corresponding
|
||||
external commands. To enable it, add @code{eshell-tramp} to
|
||||
@code{eshell-modules-list}.
|
||||
|
||||
@table @code
|
||||
|
||||
@item su
|
||||
@cmindex su
|
||||
@itemx sudo
|
||||
@cmindex sudo
|
||||
@itemx doas
|
||||
@cmindex doas
|
||||
Uses TRAMP's @command{su}, @command{sudo}, or @command{doas} method
|
||||
(@pxref{Inline methods, , , tramp, The Tramp Manual}) to run a command
|
||||
via @command{su}, @command{sudo}, or @command{doas}.
|
||||
|
||||
@end table
|
||||
|
||||
@node Extra built-in commands
|
||||
@subsection Extra built-in commands
|
||||
|
||||
This module provides several extra built-in commands documented below,
|
||||
primarily for working with lists of strings in Eshell. To enable it,
|
||||
add @code{eshell-xtra} to @code{eshell-modules-list}.
|
||||
|
||||
@table @code
|
||||
|
||||
@item expr
|
||||
@cmindex expr
|
||||
An implementation of @command{expr} using the Calc package.
|
||||
@xref{Top,,, calc, The GNU Emacs Calculator}.
|
||||
|
||||
@item intersection
|
||||
@cmindex intersection
|
||||
A wrapper around the function @code{cl-intersection} (@pxref{Lists as
|
||||
Sets,,, cl, GNU Emacs Common Lisp Emulation}). This command
|
||||
can be used for comparing lists of strings.
|
||||
|
||||
@item mismatch
|
||||
@cmindex mismatch
|
||||
A wrapper around the function @code{cl-mismatch} (@pxref{Searching
|
||||
Sequences,,, cl, GNU Emacs Common Lisp Emulation}). This command can
|
||||
be used for comparing lists of strings.
|
||||
|
||||
@item set-difference
|
||||
@cmindex set-difference
|
||||
A wrapper around the function @code{cl-set-difference} (@pxref{Lists
|
||||
as Sets,,, cl, GNU Emacs Common Lisp Emulation}). This command can be
|
||||
used for comparing lists of strings.
|
||||
|
||||
@item set-exclusive-or
|
||||
@cmindex set-exclusive-or
|
||||
A wrapper around the function @code{cl-set-exclusive-or} (@pxref{Lists
|
||||
as Sets,,, cl, GNU Emacs Common Lisp Emulation}). This command can be
|
||||
used for comparing lists of strings.
|
||||
|
||||
@item substitute
|
||||
@cmindex substitute
|
||||
A wrapper around the function @code{cl-substitute} (@pxref{Sequence
|
||||
Functions,,, cl, GNU Emacs Common Lisp Emulation}). This command can
|
||||
be used for comparing lists of strings.
|
||||
|
||||
@item union
|
||||
@cmindex union
|
||||
A wrapper around the function @code{cl-union} (@pxref{Lists as Sets,,,
|
||||
cl, GNU Emacs Common Lisp Emulation}). This command can be used for
|
||||
comparing lists of strings.
|
||||
|
||||
@end table
|
||||
|
||||
@node Writing a module
|
||||
@section Writing a module
|
||||
|
||||
An Eshell module is defined the same as any other library but with two
|
||||
additional requirements: first, the module's source file should be
|
||||
named @file{em-@var{name}.el}; second, the module must define an
|
||||
autoloaded Customize group (@pxref{Customization, , , elisp, The Emacs
|
||||
Lisp Reference Manual}) with @code{eshell-module} as the parent group.
|
||||
In order to properly autoload this group, you should wrap its
|
||||
definition with @code{progn} as follows:
|
||||
|
||||
@example
|
||||
;;;###autoload
|
||||
(progn
|
||||
(defgroup eshell-my-module nil
|
||||
"My module lets you do very cool things in Eshell."
|
||||
:tag "My module"
|
||||
:group 'eshell-module))
|
||||
@end example
|
||||
|
||||
Even if you don't have any Customize options in your module, you
|
||||
should still define the group so that Eshell can include your module
|
||||
in the Customize interface for @code{eshell-modules-list}.
|
||||
|
||||
@node Bugs and ideas
|
||||
@chapter Bugs and ideas
|
||||
@cindex reporting bugs and ideas
|
||||
@ -2158,8 +2211,6 @@ Below is a list of some known problems with Eshell version 2.4.2,
|
||||
which is the version included with Emacs 22.
|
||||
|
||||
@table @asis
|
||||
@item Documentation incomplete
|
||||
|
||||
@item Differentiate between aliases and functions
|
||||
|
||||
Allow for a Bash-compatible syntax, such as:
|
||||
@ -2227,8 +2278,6 @@ for running shells.
|
||||
|
||||
@item Implement @samp{-r}, @samp{-n} and @samp{-s} switches for @command{cp}
|
||||
|
||||
@item Make @kbd{M-5 M-x eshell} switch to ``*eshell<5>*'', creating if need be
|
||||
|
||||
@item @samp{mv @var{dir} @var{file}.tar} does not remove directories
|
||||
|
||||
This is because the tar option --remove-files doesn't do so. Should it
|
||||
|
@ -78,9 +78,9 @@ appearing in their saved logs.
|
||||
** Smarter reconnect handling for users on the move.
|
||||
ERC now offers a new, experimental reconnect strategy in the function
|
||||
'erc-server-delayed-check-reconnect', which tests for underlying
|
||||
connectivity before attempting to reconnect in earnest. See options
|
||||
'erc-server-reconnect-function' and 'erc-nickname-in-use-functions' to
|
||||
get started.
|
||||
connectivity before attempting to reconnect in earnest. See option
|
||||
'erc-server-reconnect-function' and new local module 'services-regain'
|
||||
(also experimental) to get started.
|
||||
|
||||
** Module 'fill' can add a bit of space between messages.
|
||||
On graphical displays, it's now possible to add some breathing room
|
||||
|
@ -698,7 +698,7 @@ between these modes while the user is inputting a command by hitting
|
||||
works like 'duplicate-line'. An active rectangular region is
|
||||
duplicated on its right-hand side. The new user option
|
||||
'duplicate-line-final-position' specifies where to move point
|
||||
after duplicating the line.
|
||||
after duplicating a line.
|
||||
|
||||
---
|
||||
** Files with the ".eld" extension are now visited in 'lisp-data-mode'.
|
||||
|
17
etc/PROBLEMS
17
etc/PROBLEMS
@ -1707,7 +1707,7 @@ which can be carried out at the same time:
|
||||
forwarded X connection (ssh -XC remotehostname emacs ...).
|
||||
|
||||
Keep in mind that this does not help with latency problems, only
|
||||
andwidth ones.
|
||||
bandwidth ones.
|
||||
|
||||
5) Use lbxproxy on the remote end of the connection. This is an
|
||||
interface to the low bandwidth X extension in some outdated X
|
||||
@ -2788,6 +2788,21 @@ With any of the above methods, you'd need to restart Emacs (and
|
||||
preferably also your Windows system) after making the changes, to have
|
||||
them take effect.
|
||||
|
||||
*** MinGW64 Emacs built with -D_FORTIFY_SOURCE=2 misbehaves
|
||||
|
||||
Using this preprocessor option when building Emacs with MinGW64
|
||||
produces an Emacs binary that behaves incorrectly. In particular,
|
||||
running asynchronous shell command, e.g., with 'M-&', causes Emacs to
|
||||
use 100% of CPU and start allocating a lot of memory. For the same
|
||||
reason, asynchronous native-compilation will hang Emacs (which could
|
||||
wedge Emacs during startup, if your Emacs is configured to download
|
||||
and install packages via package.el every startup). 'M-x run-python',
|
||||
'M-x shell', and similar commands also hang. Other commands might
|
||||
also cause high CPU and/or memory usage.
|
||||
|
||||
The workaround is to rebuild Emacs without the -D_FORTIFY_SOURCE=2
|
||||
option.
|
||||
|
||||
** Emacs on Windows 9X requires UNICOWS.DLL
|
||||
|
||||
If that DLL is not available, Emacs will display an error dialog
|
||||
|
@ -2419,7 +2419,7 @@ confirmation first."
|
||||
|
||||
|
||||
(defun bookmark-bmenu-locate ()
|
||||
"Display location of this bookmark. Displays in the minibuffer."
|
||||
"Display the location of the bookmark for this line."
|
||||
(interactive nil bookmark-bmenu-mode)
|
||||
(let ((bmrk (bookmark-bmenu-bookmark)))
|
||||
(message "%s" (bookmark-location bmrk))))
|
||||
|
@ -746,7 +746,8 @@ See the documentation for `calculator-mode' for more information."
|
||||
;; use 3 lines
|
||||
(let* ((bx (face-attribute 'mode-line :box))
|
||||
(lh (plist-get bx :line-width)))
|
||||
(and bx (or (not lh) (> lh 0))))
|
||||
;; Value of `:line-width' can be either a number or a cons.
|
||||
(and bx (or (not lh) (> (if (consp lh) (cdr lh) lh) 0))))
|
||||
;; if the mode line has an overline, use 3 lines
|
||||
(not (memq (face-attribute 'mode-line :overline)
|
||||
'(nil unspecified)))))))
|
||||
|
@ -49,7 +49,8 @@
|
||||
|
||||
;; To get started, type `M-x todo-show'. For full details of the user
|
||||
;; interface, commands and options, consult the Todo mode user manual,
|
||||
;; which is included in the Info documentation.
|
||||
;; which is one of the Info manuals included in the standard Emacs
|
||||
;; installation.
|
||||
|
||||
;;; Code:
|
||||
|
||||
@ -1205,7 +1206,9 @@ visiting the deleted files."
|
||||
(let ((sexp (read (buffer-substring-no-properties
|
||||
(line-beginning-position)
|
||||
(line-end-position))))
|
||||
(buffer-read-only nil))
|
||||
(buffer-read-only nil)
|
||||
(print-length nil)
|
||||
(print-level nil))
|
||||
(mapc (lambda (x) (aset (cdr x) 3 0)) sexp)
|
||||
(delete-region (line-beginning-position) (line-end-position))
|
||||
(prin1 sexp (current-buffer)))))
|
||||
@ -1294,15 +1297,15 @@ return the new category number."
|
||||
file)))
|
||||
(find-file file0)
|
||||
(let ((counts (make-vector 4 0)) ; [todo diary done archived]
|
||||
(num (1+ (length todo-categories)))
|
||||
(buffer-read-only nil))
|
||||
(num (1+ (length todo-categories))))
|
||||
(setq todo-current-todo-file file0)
|
||||
(setq todo-categories (append todo-categories
|
||||
(list (cons cat counts))))
|
||||
(widen)
|
||||
(goto-char (point-max))
|
||||
(save-excursion ; Save point for todo-category-select.
|
||||
(insert todo-category-beg cat "\n\n" todo-category-done "\n"))
|
||||
(let ((buffer-read-only nil))
|
||||
(insert todo-category-beg cat "\n\n" todo-category-done "\n")))
|
||||
(todo-update-categories-sexp)
|
||||
;; If invoked by user, display the newly added category, if
|
||||
;; called programmatically return the category number to the
|
||||
@ -1459,8 +1462,7 @@ the archive of the file moved to, creating it if it does not exist."
|
||||
(match-beginning 0)
|
||||
(point-max)))
|
||||
(content (buffer-substring-no-properties beg end))
|
||||
(counts (cdr (assoc cat todo-categories)))
|
||||
buffer-read-only)
|
||||
(counts (cdr (assoc cat todo-categories))))
|
||||
;; Move the category to the new file. Also update or create
|
||||
;; archive file if necessary.
|
||||
(with-current-buffer
|
||||
@ -1480,7 +1482,9 @@ the archive of the file moved to, creating it if it does not exist."
|
||||
nfile-short)
|
||||
(format "the category \"%s\";\n" cat)
|
||||
"enter a new category name: "))
|
||||
buffer-read-only)
|
||||
(buffer-read-only nil)
|
||||
(print-length nil)
|
||||
(print-level nil))
|
||||
(widen)
|
||||
(goto-char (point-max))
|
||||
(insert content)
|
||||
@ -1520,25 +1524,26 @@ the archive of the file moved to, creating it if it does not exist."
|
||||
;; Delete the category from the old file, and if that was the
|
||||
;; last category, delete the file. Also handle archive file
|
||||
;; if necessary.
|
||||
(remove-overlays beg end)
|
||||
(delete-region beg end)
|
||||
(goto-char (point-min))
|
||||
;; Put point after todo-categories sexp.
|
||||
(forward-line)
|
||||
(if (eobp) ; Aside from sexp, file is empty.
|
||||
(progn
|
||||
;; Skip confirming killing the archive buffer.
|
||||
(set-buffer-modified-p nil)
|
||||
(delete-file todo-current-todo-file)
|
||||
(kill-buffer)
|
||||
(when (member todo-current-todo-file todo-files)
|
||||
(todo-update-filelist-defcustoms)))
|
||||
(setq todo-categories (delete (assoc cat todo-categories)
|
||||
todo-categories))
|
||||
(todo-update-categories-sexp)
|
||||
(when (> todo-category-number (length todo-categories))
|
||||
(setq todo-category-number 1))
|
||||
(todo-category-select)))))
|
||||
(let ((buffer-read-only nil))
|
||||
(remove-overlays beg end)
|
||||
(delete-region beg end)
|
||||
(goto-char (point-min))
|
||||
;; Put point after todo-categories sexp.
|
||||
(forward-line)
|
||||
(if (eobp) ; Aside from sexp, file is empty.
|
||||
(progn
|
||||
;; Skip confirming killing the archive buffer.
|
||||
(set-buffer-modified-p nil)
|
||||
(delete-file todo-current-todo-file)
|
||||
(kill-buffer)
|
||||
(when (member todo-current-todo-file todo-files)
|
||||
(todo-update-filelist-defcustoms)))
|
||||
(setq todo-categories (delete (assoc cat todo-categories)
|
||||
todo-categories))
|
||||
(todo-update-categories-sexp)
|
||||
(when (> todo-category-number (length todo-categories))
|
||||
(setq todo-category-number 1))
|
||||
(todo-category-select))))))
|
||||
(set-window-buffer (selected-window)
|
||||
(set-buffer (find-file-noselect nfile))))))
|
||||
|
||||
@ -1706,11 +1711,19 @@ insertion provided it doesn't begin with `todo-nondiary-marker'."
|
||||
:group 'todo-edit)
|
||||
|
||||
(defcustom todo-always-add-time-string nil
|
||||
"Non-nil adds current time to a new item's date header by default.
|
||||
When the todo insertion commands have a non-nil \"maybe-notime\"
|
||||
argument, this reverses the effect of
|
||||
`todo-always-add-time-string': if t, these commands omit the
|
||||
current time, if nil, they include it."
|
||||
"Whether to add the time to an item's date header by default.
|
||||
|
||||
If non-nil, this automatically adds the current time when adding
|
||||
a new item using an insertion command without a time parameter,
|
||||
or when tagging an item as done; when adding a new item using a
|
||||
time parameter, or when editing the header of an existing todo item
|
||||
using a time parameter, typing <return> automatically inserts the
|
||||
current time.
|
||||
|
||||
When this option is nil (the default), no time string is inserted
|
||||
either automatically or when typing <return> at the time
|
||||
prompt (and in the latter case, when editing an existing time
|
||||
string, typing <return> deletes it)."
|
||||
:type 'boolean
|
||||
:group 'todo-edit)
|
||||
|
||||
@ -2314,7 +2327,6 @@ made in the number or names of categories."
|
||||
;; INC must be an integer, but users could pass it via
|
||||
;; `todo-edit-item' as e.g. `-' or `C-u'.
|
||||
(inc (prefix-numeric-value inc))
|
||||
(buffer-read-only nil)
|
||||
ndate ntime
|
||||
year monthname month day) ;; dayname
|
||||
(when marked (todo--user-error-if-marked-done-item))
|
||||
@ -2477,13 +2489,14 @@ made in the number or names of categories."
|
||||
(day day)
|
||||
(dayname nil)) ;; dayname
|
||||
(mapconcat #'eval calendar-date-display-form "")))))
|
||||
(when ndate (replace-match ndate nil nil nil 1))
|
||||
;; Add new time string to the header, if it was supplied.
|
||||
(when ntime
|
||||
(if otime
|
||||
(replace-match ntime nil nil nil 2)
|
||||
(goto-char (match-end 1))
|
||||
(insert ntime)))
|
||||
(let ((buffer-read-only nil))
|
||||
(when ndate (replace-match ndate nil nil nil 1))
|
||||
;; Add new time string to the header, if it was supplied.
|
||||
(when ntime
|
||||
(if otime
|
||||
(replace-match ntime nil nil nil 2)
|
||||
(goto-char (match-end 1))
|
||||
(insert ntime))))
|
||||
(setq todo-date-from-calendar nil)
|
||||
(setq first nil))
|
||||
;; Apply the changes to the first marked item header to the
|
||||
@ -2650,8 +2663,7 @@ meaning to raise or lower the item's priority by one."
|
||||
(1- curnum))
|
||||
((and (eq arg 'lower) (<= curnum maxnum))
|
||||
(1+ curnum))))
|
||||
candidate
|
||||
buffer-read-only)
|
||||
candidate)
|
||||
(unless (and priority
|
||||
(or (and (eq arg 'raise) (zerop priority))
|
||||
(and (eq arg 'lower) (> priority maxnum))))
|
||||
@ -2703,31 +2715,31 @@ meaning to raise or lower the item's priority by one."
|
||||
(match-string-no-properties 1)))))))
|
||||
(when match
|
||||
(user-error (concat "Cannot reprioritize items from the same "
|
||||
"category in this mode, only in Todo mode")))))
|
||||
;; Interactively or with non-nil ARG, relocate the item within its
|
||||
;; category.
|
||||
(when (or arg (called-interactively-p 'any))
|
||||
(todo-remove-item))
|
||||
(goto-char (point-min))
|
||||
(when priority
|
||||
(unless (= priority 1)
|
||||
(todo-forward-item (1- priority))
|
||||
;; When called from todo-item-undone and the highest priority
|
||||
;; is chosen, this advances point to the first done item, so
|
||||
;; move it up to the empty line above the done items
|
||||
;; separator.
|
||||
(when (looking-back (concat "^"
|
||||
(regexp-quote todo-category-done)
|
||||
"\n")
|
||||
(line-beginning-position 0))
|
||||
(todo-backward-item))))
|
||||
(todo-insert-with-overlays item)
|
||||
;; If item was marked, restore the mark.
|
||||
(and marked
|
||||
(let* ((ov (todo-get-overlay 'prefix))
|
||||
(pref (overlay-get ov 'before-string)))
|
||||
(overlay-put ov 'before-string
|
||||
(concat todo-item-mark pref))))))))
|
||||
"category in this mode, only in Todo mode")))))
|
||||
(let ((buffer-read-only nil))
|
||||
;; Interactively or with non-nil ARG, relocate the item within its
|
||||
;; category.
|
||||
(when (or arg (called-interactively-p 'any))
|
||||
(todo-remove-item))
|
||||
(goto-char (point-min))
|
||||
(when priority
|
||||
(unless (= priority 1)
|
||||
(todo-forward-item (1- priority))
|
||||
;; When called from todo-item-undone and the highest priority is
|
||||
;; chosen, this advances point to the first done item, so move
|
||||
;; it up to the empty line above the done items separator.
|
||||
(when (looking-back (concat "^"
|
||||
(regexp-quote todo-category-done)
|
||||
"\n")
|
||||
(line-beginning-position 0))
|
||||
(todo-backward-item))))
|
||||
(todo-insert-with-overlays item)
|
||||
;; If item was marked, restore the mark.
|
||||
(and marked
|
||||
(let* ((ov (todo-get-overlay 'prefix))
|
||||
(pref (overlay-get ov 'before-string)))
|
||||
(overlay-put ov 'before-string
|
||||
(concat todo-item-mark pref)))))))))
|
||||
|
||||
(defun todo-raise-item-priority ()
|
||||
"Raise priority of current item by moving it up by one item."
|
||||
@ -2768,8 +2780,7 @@ section in the category moved to."
|
||||
(save-excursion (beginning-of-line)
|
||||
(looking-at todo-category-done)))
|
||||
(not marked))
|
||||
(let* ((buffer-read-only)
|
||||
(file1 todo-current-todo-file)
|
||||
(let* ((file1 todo-current-todo-file)
|
||||
(item (todo-item-string))
|
||||
(done-item (and (todo-done-item-p) item))
|
||||
(omark (save-excursion (todo-item-start) (point-marker)))
|
||||
@ -2828,7 +2839,8 @@ section in the category moved to."
|
||||
(setq here (point))
|
||||
(while todo-items
|
||||
(todo-forward-item)
|
||||
(todo-insert-with-overlays (pop todo-items))))
|
||||
(let ((buffer-read-only nil))
|
||||
(todo-insert-with-overlays (pop todo-items)))))
|
||||
;; Move done items en bloc to top of done items section.
|
||||
(when done-items
|
||||
(todo-category-number cat2)
|
||||
@ -2842,7 +2854,8 @@ section in the category moved to."
|
||||
(forward-line)
|
||||
(unless here (setq here (point)))
|
||||
(while done-items
|
||||
(todo-insert-with-overlays (pop done-items))
|
||||
(let ((buffer-read-only nil))
|
||||
(todo-insert-with-overlays (pop done-items)))
|
||||
(todo-forward-item)))
|
||||
;; If only done items were moved, move point to the top
|
||||
;; one, otherwise, move point to the top moved todo item.
|
||||
@ -2881,12 +2894,14 @@ section in the category moved to."
|
||||
(goto-char beg)
|
||||
(while (< (point) end)
|
||||
(if (todo-marked-item-p)
|
||||
(todo-remove-item)
|
||||
(let ((buffer-read-only nil))
|
||||
(todo-remove-item))
|
||||
(todo-forward-item)))
|
||||
(setq todo-categories-with-marks
|
||||
(assq-delete-all cat1 todo-categories-with-marks)))
|
||||
(if ov (delete-overlay ov))
|
||||
(todo-remove-item))))
|
||||
(let ((buffer-read-only nil))
|
||||
(todo-remove-item)))))
|
||||
(when todo (todo-update-count 'todo (- todo) cat1))
|
||||
(when diary (todo-update-count 'diary (- diary) cat1))
|
||||
(when done (todo-update-count 'done (- done) cat1))
|
||||
@ -3015,8 +3030,7 @@ comments without asking."
|
||||
(marked (assoc cat todo-categories-with-marks))
|
||||
(num (if (not marked) 1 (cdr marked))))
|
||||
(when (or marked (todo-done-item-p))
|
||||
(let ((buffer-read-only)
|
||||
(opoint (point))
|
||||
(let ((opoint (point))
|
||||
(omark (point-marker))
|
||||
(first 'first)
|
||||
(item-count 0)
|
||||
@ -3078,19 +3092,20 @@ comments without asking."
|
||||
(when ov (delete-overlay ov))
|
||||
(if (not undone)
|
||||
(goto-char opoint)
|
||||
(if marked
|
||||
(progn
|
||||
(setq item nil)
|
||||
(re-search-forward
|
||||
(concat "^" (regexp-quote todo-category-done)) nil t)
|
||||
(while (not (eobp))
|
||||
(if (todo-marked-item-p)
|
||||
(todo-remove-item)
|
||||
(todo-forward-item)))
|
||||
(setq todo-categories-with-marks
|
||||
(assq-delete-all cat todo-categories-with-marks)))
|
||||
(goto-char omark)
|
||||
(todo-remove-item))
|
||||
(let ((buffer-read-only nil))
|
||||
(if marked
|
||||
(progn
|
||||
(setq item nil)
|
||||
(re-search-forward
|
||||
(concat "^" (regexp-quote todo-category-done)) nil t)
|
||||
(while (not (eobp))
|
||||
(if (todo-marked-item-p)
|
||||
(todo-remove-item)
|
||||
(todo-forward-item)))
|
||||
(setq todo-categories-with-marks
|
||||
(assq-delete-all cat todo-categories-with-marks)))
|
||||
(goto-char omark)
|
||||
(todo-remove-item)))
|
||||
(todo-update-count 'todo item-count)
|
||||
(todo-update-count 'done (- item-count))
|
||||
(when diary-count (todo-update-count 'diary diary-count))
|
||||
@ -3175,8 +3190,7 @@ this category does not exist in the archive, it is created."
|
||||
(concat (todo-item-string) "\n")))
|
||||
(count 0)
|
||||
(opoint (unless (todo-done-item-p) (point)))
|
||||
marked-items beg end all-done
|
||||
buffer-read-only)
|
||||
marked-items beg end all-done)
|
||||
(cond
|
||||
(all
|
||||
(if (todo-y-or-n-p "Archive all done items in this category? ")
|
||||
@ -3246,36 +3260,37 @@ this category does not exist in the archive, it is created."
|
||||
(todo-archive-mode))
|
||||
(if headers-hidden (todo-toggle-item-header))))
|
||||
(with-current-buffer tbuf
|
||||
(cond
|
||||
(all
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
;; Make sure done items are accessible.
|
||||
(widen)
|
||||
(remove-overlays beg end)
|
||||
(delete-region beg end)
|
||||
(todo-update-count 'done (- count))
|
||||
(todo-update-count 'archived count))))
|
||||
((or marked
|
||||
;; If we're archiving all done items, can't
|
||||
;; first archive item point was on, since
|
||||
;; that will short-circuit the rest.
|
||||
(and item (not all)))
|
||||
(and marked (goto-char (point-min)))
|
||||
(catch 'done
|
||||
(while (not (eobp))
|
||||
(if (or (and marked (todo-marked-item-p)) item)
|
||||
(progn
|
||||
(todo-remove-item)
|
||||
(todo-update-count 'done -1)
|
||||
(todo-update-count 'archived 1)
|
||||
;; Don't leave point below last item.
|
||||
(and (or marked item) (bolp) (eolp)
|
||||
(< (point-min) (point-max))
|
||||
(todo-backward-item))
|
||||
(when item
|
||||
(throw 'done (setq item nil))))
|
||||
(todo-forward-item))))))
|
||||
(let ((buffer-read-only nil))
|
||||
(cond
|
||||
(all
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
;; Make sure done items are accessible.
|
||||
(widen)
|
||||
(remove-overlays beg end)
|
||||
(delete-region beg end)
|
||||
(todo-update-count 'done (- count))
|
||||
(todo-update-count 'archived count))))
|
||||
((or marked
|
||||
;; If we're archiving all done items, can't
|
||||
;; first archive item point was on, since
|
||||
;; that will short-circuit the rest.
|
||||
(and item (not all)))
|
||||
(and marked (goto-char (point-min)))
|
||||
(catch 'done
|
||||
(while (not (eobp))
|
||||
(if (or (and marked (todo-marked-item-p)) item)
|
||||
(progn
|
||||
(todo-remove-item)
|
||||
(todo-update-count 'done -1)
|
||||
(todo-update-count 'archived 1)
|
||||
;; Don't leave point below last item.
|
||||
(and (or marked item) (bolp) (eolp)
|
||||
(< (point-min) (point-max))
|
||||
(todo-backward-item))
|
||||
(when item
|
||||
(throw 'done (setq item nil))))
|
||||
(todo-forward-item)))))))
|
||||
(when marked
|
||||
(setq todo-categories-with-marks
|
||||
(assq-delete-all cat todo-categories-with-marks)))
|
||||
@ -3524,7 +3539,6 @@ decreasing or increasing its number."
|
||||
(let* ((maxnum (length todo-categories))
|
||||
(prompt (format "Set category priority (1-%d): " maxnum))
|
||||
(col (current-column))
|
||||
(buffer-read-only nil)
|
||||
(priority (cond ((and (eq arg 'raise) (> curnum 1))
|
||||
(1- curnum))
|
||||
((and (eq arg 'lower) (< curnum maxnum))
|
||||
@ -3549,6 +3563,7 @@ decreasing or increasing its number."
|
||||
;; Category's name and items counts list.
|
||||
(catcons (nth (1- curnum) todo-categories))
|
||||
(todo-categories (nconc head (list catcons) tail))
|
||||
(buffer-read-only nil)
|
||||
newcats)
|
||||
(when lower (setq todo-categories (nreverse todo-categories)))
|
||||
(setq todo-categories (delete-dups todo-categories))
|
||||
@ -4875,7 +4890,9 @@ name in `todo-directory'. See also the documentation string of
|
||||
(insert-file-contents file)
|
||||
(let ((sexp (read (buffer-substring-no-properties
|
||||
(line-beginning-position)
|
||||
(line-end-position)))))
|
||||
(line-end-position))))
|
||||
(print-length nil)
|
||||
(print-level nil))
|
||||
(dolist (cat sexp)
|
||||
(let ((archive-cat (assoc (car cat) archive-sexp)))
|
||||
(if archive-cat
|
||||
@ -5056,7 +5073,9 @@ With nil or omitted CATEGORY, default to the current category."
|
||||
|
||||
(defun todo-update-categories-sexp ()
|
||||
"Update the `todo-categories' sexp at the top of the file."
|
||||
(let (buffer-read-only)
|
||||
(let ((buffer-read-only nil)
|
||||
(print-length nil)
|
||||
(print-level nil))
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
@ -5165,7 +5184,9 @@ but the categories sexp differs from the current value of
|
||||
(save-restriction
|
||||
(widen)
|
||||
(goto-char (point-min))
|
||||
(let* ((cats (prin1-to-string todo-categories))
|
||||
(let* ((print-length nil)
|
||||
(print-level nil)
|
||||
(cats (prin1-to-string todo-categories))
|
||||
(ssexp (buffer-substring-no-properties (line-beginning-position)
|
||||
(line-end-position)))
|
||||
(sexp (read ssexp)))
|
||||
|
@ -386,7 +386,7 @@ FILE can be an Org file, indicated by its \".org\" extension,
|
||||
otherwise it's assumed to be an Info file."
|
||||
(let* ((pkg-name (package-desc-name pkg-desc))
|
||||
(default-directory (package-desc-dir pkg-desc))
|
||||
(docs-directory (expand-file-name (file-name-directory file)))
|
||||
(docs-directory (file-name-directory (expand-file-name file)))
|
||||
(output (expand-file-name (format "%s.info" pkg-name)))
|
||||
clean-up)
|
||||
(when (string-match-p "\\.org\\'" file)
|
||||
|
@ -31,40 +31,41 @@
|
||||
|
||||
(defun text-property-search-forward (property &optional value predicate
|
||||
not-current)
|
||||
"Search for the next region of text where PREDICATE is true.
|
||||
PREDICATE is used to decide whether a value of PROPERTY should be
|
||||
considered as matching VALUE.
|
||||
"Search for next region of text where PREDICATE returns non-nil for PROPERTY.
|
||||
PREDICATE is used to decide whether the value of PROPERTY at a given
|
||||
buffer position should be considered as a match for VALUE.
|
||||
VALUE defaults to nil if omitted.
|
||||
|
||||
If PREDICATE is a function, it will be called with two arguments:
|
||||
VALUE and the value of PROPERTY. The function should return
|
||||
non-nil if these two values are to be considered a match.
|
||||
VALUE and the value of PROPERTY at some buffer position. The function
|
||||
should return non-nil if these two values are to be considered a match.
|
||||
|
||||
Two special values of PREDICATE can also be used:
|
||||
If PREDICATE is t, that means a value must `equal' VALUE to be
|
||||
considered a match.
|
||||
If PREDICATE is nil (which is the default value), a value will
|
||||
match if is not `equal' to VALUE. Furthermore, a nil PREDICATE
|
||||
means that the match region is ended if the value changes. For
|
||||
If PREDICATE is t, that means the value of PROPERTY must `equal' VALUE
|
||||
to be considered a match.
|
||||
If PREDICATE is nil (which is the default), the value of PROPERTY will
|
||||
match if it is not `equal' to VALUE. Furthermore, a nil PREDICATE
|
||||
means that the match region ends where the value changes. For
|
||||
instance, this means that if you loop with
|
||||
|
||||
(while (setq prop (text-property-search-forward \\='face))
|
||||
...)
|
||||
|
||||
you will get all distinct regions with non-nil `face' values in
|
||||
you will get all the distinct regions with non-nil `face' values in
|
||||
the buffer, and the `prop' object will have the details about the
|
||||
match. See the manual for more details and examples about how
|
||||
VALUE and PREDICATE interact.
|
||||
|
||||
If NOT-CURRENT is non-nil, the function will search for the first
|
||||
region that doesn't include point and has a value of PROPERTY
|
||||
that matches VALUE.
|
||||
If NOT-CURRENT is non-nil, current buffer position is not examined for
|
||||
matches: the function will search for the first region that doesn't
|
||||
include point and has a value of PROPERTY that matches VALUE.
|
||||
|
||||
If no matches can be found, return nil and don't move point.
|
||||
If found, move point to the end of the region and return a
|
||||
`prop-match' object describing the match. To access the details
|
||||
of the match, use `prop-match-beginning' and `prop-match-end' for
|
||||
the buffer positions that limit the region, and
|
||||
`prop-match-value' for the value of PROPERTY in the region."
|
||||
the buffer positions that limit the region, and `prop-match-value'
|
||||
for the value of PROPERTY in the region."
|
||||
(interactive
|
||||
(list
|
||||
(let ((string (completing-read "Search for property: " obarray)))
|
||||
@ -134,7 +135,7 @@ the buffer positions that limit the region, and
|
||||
|
||||
(defun text-property-search-backward (property &optional value predicate
|
||||
not-current)
|
||||
"Search for the previous region of text whose PROPERTY matches VALUE.
|
||||
"Search for previous region of text where PREDICATE returns non-nil for PROPERTY.
|
||||
|
||||
Like `text-property-search-forward', which see, but searches backward,
|
||||
and if a matching region is found, place point at the start of the region."
|
||||
|
@ -427,7 +427,9 @@ This only has an effect if `erc-server-auto-reconnect' is non-nil."
|
||||
If this value is too low, servers may reject your initial nick
|
||||
request upon reconnecting because they haven't yet noticed that
|
||||
your previous connection is dead. If this happens, try setting
|
||||
this value to 120 or greater."
|
||||
this value to 120 or greater and/or exploring the option
|
||||
`erc-regain-services-alist', which may provide a more proactive
|
||||
means of handling this situation on some servers."
|
||||
:type 'number)
|
||||
|
||||
(defcustom erc-server-reconnect-function 'erc-server-delayed-reconnect
|
||||
|
@ -233,10 +233,14 @@ for beeping to work."
|
||||
(const :tag "Don't beep" nil)))
|
||||
|
||||
(defcustom erc-text-matched-hook '(erc-log-matches)
|
||||
"Hook run when text matches a given match-type.
|
||||
Functions in this hook are passed as arguments:
|
||||
\(match-type nick!user@host message) where MATCH-TYPE is a symbol of:
|
||||
current-nick, keyword, pal, dangerous-host, fool."
|
||||
"Abnormal hook for visiting text matching a predefined \"type\".
|
||||
ERC calls members with the arguments (MATCH-TYPE NUH MESSAGE),
|
||||
where MATCH-TYPE is one of the symbols `current-nick', `keyword',
|
||||
`pal', `dangerous-host', `fool', and NUH is an `erc-response'
|
||||
sender, like bob!~bob@example.org. Users should keep in mind
|
||||
that MESSAGE may not include decorations, such as white space or
|
||||
time stamps, preceding the same text as inserted in the narrowed
|
||||
buffer."
|
||||
:options '(erc-log-matches erc-hide-fools erc-beep-on-match)
|
||||
:type 'hook)
|
||||
|
||||
@ -458,8 +462,19 @@ In any of the following situations, MSG is directed at an entry FOOL:
|
||||
(erc-list-match fools-end msg))))
|
||||
|
||||
(defun erc-match-message ()
|
||||
"Mark certain keywords in a region.
|
||||
Use this defun with `erc-insert-modify-hook'."
|
||||
"Add faces to matching text in inserted message."
|
||||
;; Exclude leading whitespace, stamps, etc.
|
||||
(let ((omin (point-min))
|
||||
(beg (or (and (not (get-text-property (point-min) 'erc-command))
|
||||
(next-single-property-change (point-min) 'erc-command))
|
||||
(point-min))))
|
||||
;; FIXME when ERC no longer supports 28, use `with-restriction'
|
||||
;; with `:label' here instead of passing `omin'.
|
||||
(save-restriction
|
||||
(narrow-to-region beg (point-max))
|
||||
(erc-match--message omin))))
|
||||
|
||||
(defun erc-match--message (unrestricted-point-min)
|
||||
;; This needs some refactoring.
|
||||
(goto-char (point-min))
|
||||
(let* ((to-match-nick-dep '("pal" "fool" "dangerous-host"))
|
||||
@ -561,12 +576,14 @@ Use this defun with `erc-insert-modify-hook'."
|
||||
'font-lock-face match-face)))
|
||||
;; Else twiddle your thumbs.
|
||||
(t nil))
|
||||
(run-hook-with-args
|
||||
'erc-text-matched-hook
|
||||
(intern match-type)
|
||||
(or nickuserhost
|
||||
(concat "Server:" (erc-get-parsed-vector-type vector)))
|
||||
message))))
|
||||
;; FIXME use `without-restriction' after dropping 28.
|
||||
(save-restriction
|
||||
(narrow-to-region unrestricted-point-min (point-max))
|
||||
(run-hook-with-args
|
||||
'erc-text-matched-hook (intern match-type)
|
||||
(or nickuserhost
|
||||
(concat "Server:" (erc-get-parsed-vector-type vector)))
|
||||
message)))))
|
||||
(if nickuserhost
|
||||
(append to-match-nick-dep to-match-nick-indep)
|
||||
to-match-nick-indep)))))
|
||||
|
@ -513,6 +513,127 @@ Returns t if the identify message could be sent, nil otherwise."
|
||||
nick)
|
||||
nil))
|
||||
|
||||
|
||||
;;;; Regaining nicknames
|
||||
|
||||
(defcustom erc-services-regain-alist nil
|
||||
"Alist mapping networks to nickname-regaining functions.
|
||||
This option depends on the `services-regain' module being loaded.
|
||||
Keys can also be symbols for user-provided \"context IDs\" (see
|
||||
Info node `Network Identifier'). Functions run once, when first
|
||||
establishing a logical IRC connection. Although ERC currently
|
||||
calls them with one argument, the desired but rejected nickname,
|
||||
robust user implementations should leave room for later additions
|
||||
by defining an &rest _ parameter, as well.
|
||||
|
||||
The simplest value is `erc-services-retry-nick-on-connect', which
|
||||
attempts to kill off stale connections without engaging services
|
||||
at all. Others, like `erc-services-issue-regain', and
|
||||
`erc-services-issue-ghost-and-retry-nick', only speak a
|
||||
particular flavor of NickServ. See their respective doc strings
|
||||
for details and use cases."
|
||||
:package-version '(ERC . "5.6")
|
||||
:group 'erc-hooks
|
||||
:type '(alist :key-type (symbol :tag "Network")
|
||||
:value-type
|
||||
(choice :tag "Strategy function"
|
||||
(function-item erc-services-retry-nick-on-connect)
|
||||
(function-item erc-services-issue-regain)
|
||||
(function-item erc-services-issue-ghost-and-retry-nick)
|
||||
function)))
|
||||
|
||||
(defun erc-services-retry-nick-on-connect (want)
|
||||
"Try at most once to grab nickname WANT after reconnecting.
|
||||
Expect to be used when automatically reconnecting to servers
|
||||
that are slow to abandon the previous connection.
|
||||
|
||||
Note that this strategy may only work under certain conditions,
|
||||
such as when a user's account name matches their nick."
|
||||
(erc-cmd-NICK want))
|
||||
|
||||
(defun erc-services-issue-regain (want)
|
||||
"Ask NickServ to regain nickname WANT.
|
||||
Assume WANT belongs to the user and that the services suite
|
||||
offers a \"REGAIN\" sub-command."
|
||||
(erc-cmd-MSG (concat "NickServ REGAIN " want)))
|
||||
|
||||
(defun erc-services-issue-ghost-and-retry-nick (want)
|
||||
"Ask NickServ to \"GHOST\" nickname WANT.
|
||||
After which, attempt to grab WANT before the contending party
|
||||
reconnects. Assume the ERC user owns WANT and that the server's
|
||||
services suite lacks a \"REGAIN\" command.
|
||||
|
||||
Note that this function will only work for a specific services
|
||||
implementation and is meant primarily as an example for adapting
|
||||
as needed."
|
||||
;; While heuristics based on error text may seem brittle, consider
|
||||
;; the fact that \"is not online\" has been present in Atheme's
|
||||
;; \"GHOST\" responses since at least 2005.
|
||||
(letrec ((attempts 3)
|
||||
(on-notice
|
||||
(lambda (_proc parsed)
|
||||
(when-let ((nick (erc-extract-nick
|
||||
(erc-response.sender parsed)))
|
||||
((erc-nick-equal-p nick "nickserv"))
|
||||
(contents (erc-response.contents parsed))
|
||||
(case-fold-search t)
|
||||
((string-match (rx (or "ghost" "is not online"))
|
||||
contents)))
|
||||
(setq attempts 1)
|
||||
(erc-server-send (concat "NICK " want) 'force))
|
||||
(when (zerop (cl-decf attempts))
|
||||
(remove-hook 'erc-server-NOTICE-functions on-notice t))
|
||||
nil)))
|
||||
(add-hook 'erc-server-NOTICE-functions on-notice nil t)
|
||||
(erc-message "PRIVMSG" (concat "NickServ GHOST " want))))
|
||||
|
||||
;;;###autoload(put 'services-regain 'erc--feature 'erc-services)
|
||||
(define-erc-module services-regain nil
|
||||
"Reacquire a nickname from your past self or some interloper.
|
||||
This module only concerns itself with initial nick rejections
|
||||
that occur during connection registration in response to an
|
||||
opening \"NICK\" command. More specifically, the following
|
||||
conditions must be met for ERC to activate this mechanism and
|
||||
consider its main option, `erc-services-regain-alist':
|
||||
|
||||
- the server must reject the opening \"NICK\" request
|
||||
- ERC must request a temporary nickname
|
||||
- the user must successfully authenticate
|
||||
|
||||
In practical terms, this means that this module, which is still
|
||||
somewhat experimental, is likely only useful in conjunction with
|
||||
SASL authentication rather than the traditional approach provided
|
||||
by the `services' module it shares a library with (see Info
|
||||
node `(erc) SASL' for more)."
|
||||
nil nil 'local)
|
||||
|
||||
(cl-defmethod erc--nickname-in-use-make-request
|
||||
((want string) temp &context (erc-server-connected null)
|
||||
(erc-services-regain-mode (eql t))
|
||||
(erc-services-regain-alist cons))
|
||||
"Schedule possible regain attempt upon establishing connection.
|
||||
Expect WANT to be the desired nickname and TEMP to be the current
|
||||
one."
|
||||
(letrec
|
||||
((after-connect
|
||||
(lambda (_ nick)
|
||||
(remove-hook 'erc-after-connect after-connect t)
|
||||
(when-let*
|
||||
(((equal temp nick))
|
||||
(conn (or (erc-networks--id-given erc-networks--id)
|
||||
(erc-network)))
|
||||
(found (alist-get conn erc-services-regain-alist)))
|
||||
(funcall found want))))
|
||||
(on-900
|
||||
(lambda (_ parsed)
|
||||
(remove-hook 'erc-server-900-functions on-900 t)
|
||||
(unless erc-server-connected
|
||||
(when (equal (car (erc-response.command-args parsed)) temp)
|
||||
(add-hook 'erc-after-connect after-connect nil t)))
|
||||
nil)))
|
||||
(add-hook 'erc-server-900-functions on-900 nil t))
|
||||
(cl-call-next-method))
|
||||
|
||||
(provide 'erc-services)
|
||||
|
||||
|
||||
|
@ -4930,6 +4930,10 @@ E.g. \"Read error to Nick [user@some.host]: 110\" would be shortened to
|
||||
(match-string 1 reason))
|
||||
reason))
|
||||
|
||||
(cl-defmethod erc--nickname-in-use-make-request (_nick temp)
|
||||
"Request nickname TEMP in place of rejected NICK."
|
||||
(erc-cmd-NICK temp))
|
||||
|
||||
(defun erc-nickname-in-use (nick reason)
|
||||
"If NICK is unavailable, tell the user the REASON.
|
||||
|
||||
@ -4963,7 +4967,7 @@ See also `erc-display-error-notice'."
|
||||
;; established a connection yet
|
||||
(- 9 (length erc-nick-uniquifier))))
|
||||
erc-nick-uniquifier)))
|
||||
(erc-cmd-NICK newnick)
|
||||
(erc--nickname-in-use-make-request nick newnick)
|
||||
(erc-display-error-notice
|
||||
nil
|
||||
(format "Nickname %s is %s, trying %s"
|
||||
|
@ -29,14 +29,17 @@
|
||||
(progn
|
||||
(defgroup eshell-rebind nil
|
||||
"This module allows for special keybindings that only take effect
|
||||
while the point is in a region of input text. By default, it binds
|
||||
C-a to move to the beginning of the input text (rather than just the
|
||||
beginning of the line), and C-p and C-n to move through the input
|
||||
history, C-u kills the current input text, etc. It also, if
|
||||
`eshell-confine-point-to-input' is non-nil, does not allow certain
|
||||
commands to cause the point to leave the input area, such as
|
||||
`backward-word', `previous-line', etc. This module intends to mimic
|
||||
the behavior of normal shells while the user editing new input text."
|
||||
while the point is in a region of input text. The default
|
||||
keybindings mimic the bindings used in other shells when the user
|
||||
is editing new input text.
|
||||
|
||||
For example, it binds C-u to kill the current input text and C-w
|
||||
to `backward-kill-word'. If the history module is enabled, it
|
||||
also binds C-p and C-n to move through the input history, etc.
|
||||
|
||||
If `eshell-confine-point-to-input' is non-nil, this module prevents
|
||||
certain commands from causing the point to leave the input area, such
|
||||
as `backward-word', `previous-line', etc."
|
||||
:tag "Rebind keys at input"
|
||||
:group 'eshell-module))
|
||||
|
||||
|
@ -794,19 +794,25 @@ around them. If VALUE is nil, explicitly don't draw boxes. If
|
||||
VALUE is t, draw a box with lines of width 1 in the foreground color
|
||||
of the face. If VALUE is a string, the string must be a color name,
|
||||
and the box is drawn in that color with a line width of 1. Otherwise,
|
||||
VALUE must be a property list of the form `(:line-width WIDTH
|
||||
:color COLOR :style STYLE)'. If a keyword/value pair is missing from
|
||||
the property list, a default value will be used for the value, as
|
||||
specified below. WIDTH specifies the width of the lines to draw; it
|
||||
defaults to 1. If WIDTH is negative, the absolute value is the width
|
||||
of the lines, and draw top/bottom lines inside the characters area,
|
||||
not around it. COLOR is the name of the color to draw in, default is
|
||||
the background color of the face for 3D boxes and `flat-button', and
|
||||
the foreground color of the face for other boxes. STYLE specifies
|
||||
whether a 3D box should be draw. If STYLE is `released-button', draw
|
||||
a box looking like a released 3D button. If STYLE is `pressed-button'
|
||||
draw a box that appears like a pressed button. If STYLE is nil,
|
||||
`flat-button' or omitted, draw a 2D box.
|
||||
VALUE must be a property list of the following form:
|
||||
|
||||
(:line-width WIDTH :color COLOR :style STYLE)
|
||||
|
||||
If a keyword/value pair is missing from the property list, a default
|
||||
value will be used for the value, as specified below.
|
||||
|
||||
WIDTH specifies the width of the lines to draw; it defaults to 1.
|
||||
If WIDTH is negative, the absolute value is the width of the lines,
|
||||
and draw top/bottom lines inside the characters area, not around it.
|
||||
WIDTH can also be a cons (VWIDTH . HWIDTH), which specifies different
|
||||
values for the vertical and the horizontal line width.
|
||||
COLOR is the name of the color to use for the box lines, default is
|
||||
the background color of the face for 3D and `flat-button' boxes, and
|
||||
the foreground color of the face for the other boxes.
|
||||
STYLE specifies whether a 3D box should be drawn. If STYLE
|
||||
is `released-button', draw a box looking like a released 3D button.
|
||||
If STYLE is `pressed-button', draw a box that looks like a pressed
|
||||
button. If STYLE is nil, `flat-button', or omitted, draw a 2D box.
|
||||
|
||||
`:inverse-video'
|
||||
|
||||
|
@ -742,13 +742,14 @@ message."
|
||||
(setq rmail-summary-buffer nil)))
|
||||
(save-excursion
|
||||
(let ((rbuf (current-buffer))
|
||||
(total rmail-total-messages))
|
||||
(total 0))
|
||||
(set-buffer sumbuf)
|
||||
;; Set up the summary buffer's contents.
|
||||
(let ((buffer-read-only nil))
|
||||
(erase-buffer)
|
||||
(while summary-msgs
|
||||
(princ (cdr (car summary-msgs)) sumbuf)
|
||||
(setq total (1+ total))
|
||||
(setq summary-msgs (cdr summary-msgs)))
|
||||
(goto-char (point-min)))
|
||||
;; Set up the rest of its state and local variables.
|
||||
|
@ -227,10 +227,18 @@ in the tool bar will close the current window where possible."
|
||||
'(menu-item "Insert File..." insert-file
|
||||
:enable (menu-bar-non-minibuffer-window-p)
|
||||
:help "Insert another file into current buffer"))
|
||||
(bindings--define-key menu [project-dired]
|
||||
'(menu-item "Open Project Directory" project-dired
|
||||
:enable (menu-bar-non-minibuffer-window-p)
|
||||
:help "Read the root directory of the current project, to operate on its files"))
|
||||
(bindings--define-key menu [dired]
|
||||
'(menu-item "Open Directory..." dired
|
||||
:enable (menu-bar-non-minibuffer-window-p)
|
||||
:help "Read a directory, to operate on its files"))
|
||||
(bindings--define-key menu [project-open-file]
|
||||
'(menu-item "Open File In Project..." project-find-file
|
||||
:enable (menu-bar-non-minibuffer-window-p)
|
||||
:help "Read existing file that belongs to current project into an Emacs buffer"))
|
||||
(bindings--define-key menu [open-file]
|
||||
'(menu-item "Open File..." menu-find-file-existing
|
||||
:enable (menu-bar-non-minibuffer-window-p)
|
||||
@ -355,6 +363,9 @@ in the tool bar will close the current window where possible."
|
||||
(bindings--define-key menu [tags-srch]
|
||||
'(menu-item "Search Tagged Files..." tags-search
|
||||
:help "Search for a regexp in all tagged files"))
|
||||
(bindings--define-key menu [project-search]
|
||||
'(menu-item "Search in Project Files..." project-find-regexp
|
||||
:help "Search for a regexp in files belonging to current project"))
|
||||
(bindings--define-key menu [separator-tag-search] menu-bar-separator)
|
||||
|
||||
(bindings--define-key menu [repeat-search-back]
|
||||
@ -406,6 +417,9 @@ in the tool bar will close the current window where possible."
|
||||
(bindings--define-key menu [tags-repl]
|
||||
'(menu-item "Replace in Tagged Files..." tags-query-replace
|
||||
:help "Interactively replace a regexp in all tagged files"))
|
||||
(bindings--define-key menu [project-replace]
|
||||
'(menu-item "Replace in Project Files..." project-query-replace-regexp
|
||||
:help "Interactively replace a regexp in files belonging to current project"))
|
||||
(bindings--define-key menu [separator-replace-tags]
|
||||
menu-bar-separator)
|
||||
|
||||
@ -1759,8 +1773,12 @@ mail status in mode line"))
|
||||
|
||||
(defvar menu-bar-shell-commands-menu
|
||||
(let ((menu (make-sparse-keymap "Shell Commands")))
|
||||
(bindings--define-key menu [project-interactive-shell]
|
||||
'(menu-item "Run Shell In Project" project-shell
|
||||
:help "Run a subshell interactively, in the current project's root directory"))
|
||||
|
||||
(bindings--define-key menu [interactive-shell]
|
||||
'(menu-item "Run Shell Interactively" shell
|
||||
'(menu-item "Run Shell" shell
|
||||
:help "Run a subshell interactively"))
|
||||
|
||||
(bindings--define-key menu [async-shell-command]
|
||||
@ -1778,6 +1796,31 @@ mail status in mode line"))
|
||||
|
||||
menu))
|
||||
|
||||
(defvar menu-bar-project-menu
|
||||
(let ((menu (make-sparse-keymap "Project")))
|
||||
(bindings--define-key menu [project-execute-extended-command] '(menu-item "Execute Extended Command..." project-execute-extended-command :help "Execute an extended command in project root directory"))
|
||||
(bindings--define-key menu [project-query-replace-regexp] '(menu-item "Query Replace Regexp..." project-query-replace-regexp :help "Interactively replace a regexp in files belonging to current project"))
|
||||
(bindings--define-key menu [project-or-external-find-regexp] '(menu-item "Find Regexp Including External Roots..." project-or-external-find-regexp :help "Search for a regexp in files belonging to current project or external files"))
|
||||
(bindings--define-key menu [project-find-regexp] '(menu-item "Find Regexp..." project-find-regexp :help "Search for a regexp in files belonging to current project"))
|
||||
(bindings--define-key menu [separator-project-search] menu-bar-separator)
|
||||
(bindings--define-key menu [project-kill-buffers] '(menu-item "Kill Buffers..." project-kill-buffers :help "Kill the buffers belonging to the current project"))
|
||||
(bindings--define-key menu [project-list-buffers] '(menu-item "List Buffers..." project-list-buffers :help "Pop up a window listing all Emacs buffers belonging to current project"))
|
||||
(bindings--define-key menu [project-switch-to-buffer] '(menu-item "Switch To Buffer..." project-switch-to-buffer :help "Prompt for a buffer belonging to current project, and switch to it"))
|
||||
(bindings--define-key menu [separator-project-buffers] menu-bar-separator)
|
||||
(bindings--define-key menu [project-async-shell-command] '(menu-item "Async Shell Command..." project-async-shell-command :help "Invoke a shell command in project root asynchronously in background"))
|
||||
(bindings--define-key menu [project-shell-command] '(menu-item "Shell Command..." project-shell-command :help "Invoke a shell command in project root and catch its output"))
|
||||
(bindings--define-key menu [project-eshell] '(menu-item "Run Eshell" project-eshell :help "Run eshell for the current project"))
|
||||
(bindings--define-key menu [project-shell] '(menu-item "Run Shell" project-shell :help "Run a subshell interactively, in the current project's root directory"))
|
||||
(bindings--define-key menu [project-compile] '(menu-item "Compile..." project-compile :help "Invoke compiler or Make for current project, view errors"))
|
||||
(bindings--define-key menu [separator-project-programs] menu-bar-separator)
|
||||
(bindings--define-key menu [project-switch-project] '(menu-item "Switch Project..." project-switch-project :help "Switch to another project and then run a command"))
|
||||
(bindings--define-key menu [project-vc-dir] '(menu-item "VC Dir..." project-vc-dir :help "Show the VC status of the project repository"))
|
||||
(bindings--define-key menu [project-dired] '(menu-item "Open Project Root" project-dired :help "Read the root directory of the current project, to operate on its files"))
|
||||
(bindings--define-key menu [project-find-dir] '(menu-item "Open Directory..." project-find-dir :help "Open existing directory that belongs to current project"))
|
||||
(bindings--define-key menu [project-or-external-find-file] '(menu-item "Open File Including External Roots..." project-or-external-find-file :help "Open existing file that belongs to current project or its external roots"))
|
||||
(bindings--define-key menu [project-open-file] '(menu-item "Open File..." project-find-file :help "Open an existing file that belongs to current project"))
|
||||
menu))
|
||||
|
||||
(defun menu-bar-read-mail ()
|
||||
"Read mail using `read-mail-command'."
|
||||
(interactive)
|
||||
@ -1864,6 +1907,9 @@ mail status in mode line"))
|
||||
'(menu-item "Language Server Support (Eglot)" eglot
|
||||
:help "Start language server suitable for this buffer's major-mode"))
|
||||
|
||||
(bindings--define-key menu [project]
|
||||
`(menu-item "Project" ,menu-bar-project-menu))
|
||||
|
||||
(bindings--define-key menu [ede]
|
||||
'(menu-item "Project Support (EDE)"
|
||||
global-ede-mode
|
||||
@ -1873,9 +1919,13 @@ mail status in mode line"))
|
||||
(bindings--define-key menu [gdb]
|
||||
'(menu-item "Debugger (GDB)..." gdb
|
||||
:help "Debug a program from within Emacs with GDB"))
|
||||
(bindings--define-key menu [project-compile]
|
||||
'(menu-item "Compile Project..." project-compile
|
||||
:help "Invoke compiler or Make for current project, view errors"))
|
||||
|
||||
(bindings--define-key menu [compile]
|
||||
'(menu-item "Compile..." compile
|
||||
:help "Invoke compiler or Make, view compilation errors"))
|
||||
:help "Invoke compiler or Make in current buffer's directory, view errors"))
|
||||
|
||||
(bindings--define-key menu [shell-commands]
|
||||
`(menu-item "Shell Commands"
|
||||
@ -2369,7 +2419,17 @@ Buffers menu is regenerated."
|
||||
'menu-item
|
||||
"List All Buffers"
|
||||
'list-buffers
|
||||
:help "Pop up a window listing all Emacs buffers"))
|
||||
:help "Pop up a window listing all Emacs buffers")
|
||||
(list 'select-buffer-in-project
|
||||
'menu-item
|
||||
"Select Buffer In Project..."
|
||||
'project-switch-to-buffer
|
||||
:help "Prompt for a buffer belonging to current project, and switch to it")
|
||||
(list 'list-buffers-in-project
|
||||
'menu-item
|
||||
"List Buffers In Project..."
|
||||
'project-list-buffers
|
||||
:help "Pop up a window listing all Emacs buffers belonging to current project"))
|
||||
"Entries to be included at the end of the \"Buffers\" menu.")
|
||||
|
||||
(defvar menu-bar-select-buffer-function 'switch-to-buffer
|
||||
|
39
lisp/misc.el
39
lisp/misc.el
@ -64,7 +64,7 @@ Also see the `duplicate-line' command."
|
||||
(insert string)))
|
||||
|
||||
(defcustom duplicate-line-final-position 0
|
||||
"Where to put point after duplicating the line with `duplicate-line'.
|
||||
"Where to put point after `duplicate-line' or `duplicate-dwim'.
|
||||
When 0, leave point on the original line.
|
||||
When 1, move point to the first new line.
|
||||
When -1, move point to the last new line.
|
||||
@ -105,7 +105,18 @@ Also see the `copy-from-above-command' command."
|
||||
(forward-line duplicate-line-final-position)
|
||||
(move-to-column col))))
|
||||
|
||||
(declare-function rectangle--duplicate-right "rect" (n))
|
||||
(defcustom duplicate-region-final-position 0
|
||||
"Where the region ends up after duplicating a region with `duplicate-dwim'.
|
||||
When 0, leave the region in place.
|
||||
When 1, put the region around the first copy.
|
||||
When -1, put the region around the last copy."
|
||||
:type '(choice (const :tag "Leave region in place" 0)
|
||||
(const :tag "Put region around first copy" 1)
|
||||
(const :tag "Put region around last copy" -1))
|
||||
:group 'editing
|
||||
:version "29.1")
|
||||
|
||||
(declare-function rectangle--duplicate-right "rect" (n displacement))
|
||||
|
||||
;; `duplicate-dwim' preserves an active region and changes the buffer
|
||||
;; outside of it: disregard the region when immediately undoing the
|
||||
@ -118,24 +129,40 @@ Also see the `copy-from-above-command' command."
|
||||
If the region is inactive, duplicate the current line (like `duplicate-line').
|
||||
Otherwise, duplicate the region, which remains active afterwards.
|
||||
If the region is rectangular, duplicate on its right-hand side.
|
||||
Interactively, N is the prefix numeric argument, and defaults to 1."
|
||||
Interactively, N is the prefix numeric argument, and defaults to 1.
|
||||
The variables `duplicate-line-final-position' and
|
||||
`duplicate-region-final-position' control the position of point
|
||||
and the region after the duplication."
|
||||
(interactive "p")
|
||||
(unless n
|
||||
(setq n 1))
|
||||
(cond
|
||||
((<= n 0) nil)
|
||||
;; Duplicate rectangle.
|
||||
((bound-and-true-p rectangle-mark-mode)
|
||||
(rectangle--duplicate-right n)
|
||||
(rectangle--duplicate-right n
|
||||
(if (< duplicate-region-final-position 0)
|
||||
n
|
||||
duplicate-region-final-position))
|
||||
(setq deactivate-mark nil))
|
||||
|
||||
;; Duplicate (contiguous) region.
|
||||
((use-region-p)
|
||||
(let* ((beg (region-beginning))
|
||||
(end (region-end))
|
||||
(text (buffer-substring beg end)))
|
||||
(text (buffer-substring beg end))
|
||||
(pt (point))
|
||||
(mk (mark)))
|
||||
(save-excursion
|
||||
(goto-char end)
|
||||
(duplicate--insert-copies n text)))
|
||||
(duplicate--insert-copies n text))
|
||||
(let* ((displace (if (< duplicate-region-final-position 0)
|
||||
n
|
||||
duplicate-region-final-position))
|
||||
(d (* displace (- end beg))))
|
||||
(unless (zerop d)
|
||||
(push-mark (+ mk d))
|
||||
(goto-char (+ pt d)))))
|
||||
(setq deactivate-mark nil))
|
||||
|
||||
;; Duplicate line.
|
||||
|
@ -1068,6 +1068,8 @@ file names."
|
||||
(when (and (file-directory-p newname)
|
||||
(not (directory-name-p newname)))
|
||||
(tramp-error v 'file-error "File is a directory %s" newname))
|
||||
(when (file-regular-p newname)
|
||||
(delete-file newname))
|
||||
|
||||
(cond
|
||||
;; We cannot rename volatile files, as used by Google-drive.
|
||||
|
@ -226,6 +226,7 @@ file names."
|
||||
|
||||
(let ((t1 (tramp-tramp-file-p filename))
|
||||
(t2 (tramp-tramp-file-p newname))
|
||||
(equal-remote (tramp-equal-remote filename newname))
|
||||
(rclone-operation (if (eq op 'copy) "copyto" "moveto"))
|
||||
(msg-operation (if (eq op 'copy) "Copying" "Renaming")))
|
||||
|
||||
@ -236,8 +237,12 @@ file names."
|
||||
(when (and (file-directory-p newname)
|
||||
(not (directory-name-p newname)))
|
||||
(tramp-error v 'file-error "File is a directory %s" newname))
|
||||
(when (file-regular-p newname)
|
||||
(delete-file newname))
|
||||
|
||||
(if (or (and t1 (not (tramp-rclone-file-name-p filename)))
|
||||
(if (or (and equal-remote
|
||||
(tramp-get-connection-property v "direct-copy-failed"))
|
||||
(and t1 (not (tramp-rclone-file-name-p filename)))
|
||||
(and t2 (not (tramp-rclone-file-name-p newname))))
|
||||
|
||||
;; We cannot copy or rename directly.
|
||||
@ -257,9 +262,20 @@ file names."
|
||||
v rclone-operation
|
||||
(tramp-rclone-remote-file-name filename)
|
||||
(tramp-rclone-remote-file-name newname)))
|
||||
(tramp-error
|
||||
v 'file-error
|
||||
"Error %s `%s' `%s'" msg-operation filename newname)))
|
||||
(if (or (not equal-remote)
|
||||
(and equal-remote
|
||||
(tramp-get-connection-property
|
||||
v "direct-copy-failed")))
|
||||
(tramp-error
|
||||
v 'file-error
|
||||
"Error %s `%s' `%s'" msg-operation filename newname)
|
||||
|
||||
;; Some WebDAV server, like the one from QNAP, do
|
||||
;; not support direct copy/move. Try a fallback.
|
||||
(tramp-set-connection-property v "direct-copy-failed" t)
|
||||
(tramp-rclone-do-copy-or-rename-file
|
||||
op filename newname ok-if-already-exists keep-date
|
||||
preserve-uid-gid preserve-extended-attributes))))
|
||||
|
||||
(when (and t1 (eq op 'rename))
|
||||
(while (file-exists-p filename)
|
||||
|
@ -1955,8 +1955,11 @@ version, the function does nothing."
|
||||
"Return contents of BUFFER.
|
||||
If BUFFER is not a buffer or a buffer name, return the contents
|
||||
of `current-buffer'."
|
||||
(with-current-buffer (or buffer (current-buffer))
|
||||
(substring-no-properties (buffer-string))))
|
||||
(or (let ((buf (or buffer (current-buffer))))
|
||||
(when (bufferp buf)
|
||||
(with-current-buffer (or buffer (current-buffer))
|
||||
(substring-no-properties (buffer-string)))))
|
||||
""))
|
||||
|
||||
(defun tramp-debug-buffer-name (vec)
|
||||
"A name for the debug buffer for VEC."
|
||||
|
@ -1330,7 +1330,9 @@ subsequent attributes. This regexp does not have capture groups.")
|
||||
`(sequence "("
|
||||
(0+ (any "$@%&*;\\[]"))
|
||||
")")
|
||||
"A regular expression for a subroutine prototype. Not as strict as the actual prototype syntax, but good enough to distinguish prototypes from signatures.")
|
||||
"A regular expression for a subroutine prototype. Not as strict
|
||||
as the actual prototype syntax, but good enough to distinguish
|
||||
prototypes from signatures.")
|
||||
|
||||
(defconst cperl--signature-rx
|
||||
`(sequence "("
|
||||
@ -1347,11 +1349,22 @@ subsequent attributes. This regexp does not have capture groups.")
|
||||
(optional (sequence ,cperl--ws*-rx) "," )
|
||||
,cperl--ws*-rx
|
||||
")")
|
||||
"A regular expression for a subroutine signature.
|
||||
"A rx sequence subroutine signature without initializers.
|
||||
These are a bit more restricted than \"my\" declaration lists
|
||||
because they allow only one slurpy variable, and only in the last
|
||||
place.")
|
||||
|
||||
(defconst cperl--sloppy-signature-rx
|
||||
`(sequence "("
|
||||
,cperl--ws*-rx
|
||||
(or ,cperl--basic-scalar-rx
|
||||
,cperl--basic-array-rx
|
||||
,cperl--basic-hash-rx)
|
||||
,cperl--ws*-rx
|
||||
(or "," "=" "||=" "//=" ")"))
|
||||
"A rx sequence for the begin of a signature with initializers.
|
||||
Initializers can contain almost all Perl constructs and thus can not be covered by regular expressions. This sequence captures enough to distinguish a signature from a prototype.")
|
||||
|
||||
(defconst cperl--package-rx
|
||||
`(sequence (group "package")
|
||||
,cperl--ws+-rx
|
||||
@ -2853,10 +2866,13 @@ Will not look before LIM."
|
||||
;; Back up over label lines, since they don't
|
||||
;; affect whether our line is a continuation.
|
||||
;; (Had \, too)
|
||||
(while (and (eq (preceding-char) ?:)
|
||||
(while (save-excursion
|
||||
(and (eq (preceding-char) ?:)
|
||||
(re-search-backward
|
||||
(rx (sequence (eval cperl--label-rx) point))
|
||||
nil t))
|
||||
nil t)
|
||||
;; Ignore if in comment or RE
|
||||
(not (nth 3 (syntax-ppss)))))
|
||||
;; This is always FALSE?
|
||||
(if (eq (preceding-char) ?\,)
|
||||
;; Will go to beginning of line, essentially.
|
||||
@ -3116,7 +3132,8 @@ and closing parentheses and brackets."
|
||||
;; Now it is a hash reference
|
||||
(+ cperl-indent-level cperl-close-paren-offset))
|
||||
;; Labels do not take :: ...
|
||||
(if (looking-at "\\(\\w\\|_\\)+[ \t]*:[^:]")
|
||||
(if (and (looking-at "\\(\\w\\|_\\)+[ \t]*:[^:]")
|
||||
(not (looking-at (rx (eval cperl--false-label-rx)))))
|
||||
(if (> (current-indentation) cperl-min-label-indent)
|
||||
(- (current-indentation) cperl-label-offset)
|
||||
;; Do not move `parse-data', this should
|
||||
@ -3539,7 +3556,7 @@ position of the end of the unsafe construct."
|
||||
(setq end (point)))))
|
||||
(or end pos)))))
|
||||
|
||||
(defun cperl-find-sub-attrs (&optional st-l b-fname e-fname pos)
|
||||
(defun cperl-find-sub-attrs (&optional st-l _b-fname _e-fname pos)
|
||||
"Syntactically mark (and fontify) attributes of a subroutine.
|
||||
Should be called with the point before leading colon of an attribute."
|
||||
;; Works *before* syntax recognition is done
|
||||
@ -3608,7 +3625,6 @@ Should be called with the point before leading colon of an attribute."
|
||||
'attrib-group (if (looking-at "{") t 0))
|
||||
(and pos
|
||||
(progn
|
||||
(< 1 (count-lines (+ 3 pos) (point))) ; end of `sub'
|
||||
;; Apparently, we do not need `multiline': faces added now
|
||||
(put-text-property (+ 3 pos) (cperl-1+ (point))
|
||||
'syntax-type 'sub-decl))))
|
||||
@ -5919,40 +5935,46 @@ default function."
|
||||
;; statement ends in a "{" (definition) or ";"
|
||||
;; (declaration without body)
|
||||
(list (concat "\\<" cperl-sub-regexp
|
||||
;; group 1: optional subroutine name
|
||||
(rx
|
||||
(sequence (eval cperl--ws+-rx)
|
||||
(group (optional (eval cperl--normal-identifier-rx)))))
|
||||
;; "\\([^ \n\t{;()]+\\)" ; 2=name (assume non-anonymous)
|
||||
(rx
|
||||
(optional
|
||||
(group (sequence (group (eval cperl--ws*-rx))
|
||||
(eval cperl--prototype-rx)))))
|
||||
;; "\\("
|
||||
;; cperl-maybe-white-and-comment-rex ;whitespace/comments?
|
||||
;; "([^()]*)\\)?" ; prototype
|
||||
(group (optional
|
||||
(eval cperl--normal-identifier-rx)))))
|
||||
;; "fontified" elsewhere: Prototype
|
||||
(rx (optional
|
||||
(sequence (eval cperl--ws*-rx)
|
||||
(eval cperl--prototype-rx))))
|
||||
;; fontified elsewhere: Attributes
|
||||
(rx (optional (sequence (eval cperl--ws*-rx)
|
||||
(eval cperl--attribute-list-rx))))
|
||||
; cperl-maybe-white-and-comment-rex ; whitespace/comments?
|
||||
(rx (group-n 3
|
||||
(optional (sequence(eval cperl--ws*-rx)
|
||||
(eval cperl--signature-rx)))))
|
||||
(rx (eval cperl--ws*-rx))
|
||||
"[{;]")
|
||||
'(1 (if (eq (char-after (cperl-1- (match-end 0))) ?\{ )
|
||||
'font-lock-function-name-face
|
||||
'font-lock-variable-name-face)
|
||||
;; group 2: Identifies the start of the anchor
|
||||
(rx (group
|
||||
(or (group-n 3 ";") ; Either a declaration...
|
||||
"{" ; ... or a code block
|
||||
;; ... or a complete signature
|
||||
(sequence (eval cperl--signature-rx)
|
||||
(eval cperl--ws*-rx))
|
||||
;; ... or the start of a "sloppy" signature
|
||||
(sequence (eval cperl--sloppy-signature-rx)
|
||||
;; arbtrarily continue "a few lines"
|
||||
(repeat 0 200 (not (in "{"))))))))
|
||||
'(1 (if (match-beginning 3)
|
||||
'font-lock-variable-name-face
|
||||
'font-lock-function-name-face)
|
||||
t ;; override
|
||||
t) ;; laxmatch in case of anonymous subroutines
|
||||
;; -------- anchored: Signature
|
||||
`(,(rx (or (eval cperl--basic-scalar-rx)
|
||||
(eval cperl--basic-array-rx)
|
||||
(eval cperl--basic-hash-rx)))
|
||||
`(,(rx (sequence (in "(,")
|
||||
(eval cperl--ws*-rx)
|
||||
(group (or (eval cperl--basic-scalar-rx)
|
||||
(eval cperl--basic-array-rx)
|
||||
(eval cperl--basic-hash-rx)))))
|
||||
(progn
|
||||
(goto-char (match-beginning 3)) ; pre-match: Back to sig
|
||||
(match-end 3))
|
||||
|
||||
(goto-char (match-beginning 2)) ; pre-match: Back to sig
|
||||
(match-end 2))
|
||||
nil
|
||||
(0 font-lock-variable-name-face)))
|
||||
(1 font-lock-variable-name-face)))
|
||||
;; -------- various stuff calling for a package name
|
||||
;; (matcher subexp facespec)
|
||||
`(,(rx (sequence symbol-start
|
||||
|
@ -3196,49 +3196,51 @@ for which LSP on-type-formatting should be requested."
|
||||
((:documentation sigdoc)) parameters activeParameter)
|
||||
sig
|
||||
(with-temp-buffer
|
||||
(save-excursion (insert siglabel))
|
||||
(insert siglabel)
|
||||
;; Ad-hoc attempt to parse label as <name>(<params>)
|
||||
(when (looking-at "\\([^(]*\\)(\\([^)]+\\))")
|
||||
(add-face-text-property (match-beginning 1) (match-end 1)
|
||||
'font-lock-function-name-face))
|
||||
;; Add documentation, indented so we can distinguish multiple signatures
|
||||
(when-let (doc (and (not briefp) sigdoc (eglot--format-markup sigdoc)))
|
||||
(goto-char (point-max))
|
||||
(insert "\n" (replace-regexp-in-string "^" " " doc)))
|
||||
;; Now to the parameters
|
||||
(cl-loop
|
||||
with active-param = (or sig-active activeParameter)
|
||||
for i from 0 for parameter across parameters do
|
||||
(eglot--dbind ((ParameterInformation)
|
||||
((:label parlabel))
|
||||
((:documentation pardoc)))
|
||||
parameter
|
||||
;; ...perhaps highlight it in the formals list
|
||||
(when (and (eq i active-param))
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(pcase-let
|
||||
((`(,beg ,end)
|
||||
(if (stringp parlabel)
|
||||
(let ((case-fold-search nil))
|
||||
(and (search-forward parlabel (line-end-position) t)
|
||||
(list (match-beginning 0) (match-end 0))))
|
||||
(mapcar #'1+ (append parlabel nil)))))
|
||||
(if (and beg end)
|
||||
(add-face-text-property
|
||||
beg end
|
||||
'eldoc-highlight-function-argument)))))
|
||||
;; ...and/or maybe add its doc on a line by its own.
|
||||
(let (fpardoc)
|
||||
(when (and pardoc (not briefp)
|
||||
(not (string-empty-p
|
||||
(setq fpardoc (eglot--format-markup pardoc)))))
|
||||
(insert "\n "
|
||||
(propertize
|
||||
(if (stringp parlabel) parlabel
|
||||
(apply #'substring siglabel (mapcar #'1+ parlabel)))
|
||||
'face (and (eq i active-param) 'eldoc-highlight-function-argument))
|
||||
": " fpardoc)))))
|
||||
;; Add documentation, indented so we can distinguish multiple signatures
|
||||
(when-let (doc (and (not briefp) sigdoc (eglot--format-markup sigdoc)))
|
||||
(goto-char (point-max))
|
||||
(insert "\n" (replace-regexp-in-string "^" " " doc)))
|
||||
;; Now to the parameters
|
||||
(cl-loop
|
||||
with active-param = (or sig-active activeParameter)
|
||||
for i from 0 for parameter across parameters do
|
||||
(eglot--dbind ((ParameterInformation)
|
||||
((:label parlabel))
|
||||
((:documentation pardoc)))
|
||||
parameter
|
||||
(when (zerop i)
|
||||
(goto-char (elt parlabel 0))
|
||||
(search-backward "(" nil t)
|
||||
(add-face-text-property (point-min) (point)
|
||||
'font-lock-function-name-face))
|
||||
;; ...perhaps highlight it in the formals list
|
||||
(when (= i active-param)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(pcase-let
|
||||
((`(,beg ,end)
|
||||
(if (stringp parlabel)
|
||||
(let ((case-fold-search nil))
|
||||
(and (search-forward parlabel (line-end-position) t)
|
||||
(list (match-beginning 0) (match-end 0))))
|
||||
(mapcar #'1+ (append parlabel nil)))))
|
||||
(if (and beg end)
|
||||
(add-face-text-property
|
||||
beg end
|
||||
'eldoc-highlight-function-argument)))))
|
||||
;; ...and/or maybe add its doc on a line by its own.
|
||||
(let (fpardoc)
|
||||
(when (and pardoc (not briefp)
|
||||
(not (string-empty-p
|
||||
(setq fpardoc (eglot--format-markup pardoc)))))
|
||||
(insert "\n "
|
||||
(propertize
|
||||
(if (stringp parlabel) parlabel
|
||||
(apply #'substring siglabel (mapcar #'1+ parlabel)))
|
||||
'face (and (eq i active-param) 'eldoc-highlight-function-argument))
|
||||
": " fpardoc)))))
|
||||
(buffer-string))))
|
||||
|
||||
(defun eglot-signature-eldoc-function (cb)
|
||||
@ -3348,9 +3350,11 @@ for which LSP on-type-formatting should be requested."
|
||||
(mapcar (lambda (c) (apply #'dfs c)) children))))))
|
||||
(mapcar (lambda (s) (apply #'dfs s)) res)))
|
||||
|
||||
(defun eglot-imenu ()
|
||||
(cl-defun eglot-imenu ()
|
||||
"Eglot's `imenu-create-index-function'.
|
||||
Returns a list as described in docstring of `imenu--index-alist'."
|
||||
(unless (eglot--server-capable :textDocument/documentSymbol)
|
||||
(cl-return-from eglot-imenu))
|
||||
(let* ((res (eglot--request (eglot--current-server-or-lose)
|
||||
:textDocument/documentSymbol
|
||||
`(:textDocument
|
||||
|
@ -1024,38 +1024,45 @@ Return the pitem of the function we went to the beginning of."
|
||||
"Helper function for `js-beginning-of-defun'."
|
||||
(let ((pstate (js--beginning-of-defun-raw)))
|
||||
(when pstate
|
||||
(goto-char (js--pitem-h-begin (car pstate))))))
|
||||
(goto-char (js--pitem-h-begin (car pstate)))
|
||||
t)))
|
||||
|
||||
(defun js-beginning-of-defun (&optional arg)
|
||||
"Value of `beginning-of-defun-function' for `js-mode'."
|
||||
(setq arg (or arg 1))
|
||||
(while (and (not (eobp)) (< arg 0))
|
||||
(cl-incf arg)
|
||||
(when (and (not js-flat-functions)
|
||||
(or (eq (js-syntactic-context) 'function)
|
||||
(js--function-prologue-beginning)))
|
||||
(js-end-of-defun))
|
||||
(let ((found))
|
||||
(while (and (not (eobp)) (< arg 0))
|
||||
(cl-incf arg)
|
||||
(when (and (not js-flat-functions)
|
||||
(or (eq (js-syntactic-context) 'function)
|
||||
(js--function-prologue-beginning)))
|
||||
(js-end-of-defun))
|
||||
|
||||
(if (js--re-search-forward
|
||||
"\\_<function\\_>" nil t)
|
||||
(goto-char (js--function-prologue-beginning))
|
||||
(goto-char (point-max))))
|
||||
(if (js--re-search-forward
|
||||
"\\_<function\\_>" nil t)
|
||||
(progn (goto-char (js--function-prologue-beginning))
|
||||
(setq found t))
|
||||
(goto-char (point-max))
|
||||
(setq found nil)))
|
||||
|
||||
(while (> arg 0)
|
||||
(cl-decf arg)
|
||||
;; If we're just past the end of a function, the user probably wants
|
||||
;; to go to the beginning of *that* function
|
||||
(when (eq (char-before) ?})
|
||||
(backward-char))
|
||||
(while (> arg 0)
|
||||
(cl-decf arg)
|
||||
;; If we're just past the end of a function, the user probably wants
|
||||
;; to go to the beginning of *that* function
|
||||
(when (eq (char-before) ?})
|
||||
(backward-char))
|
||||
|
||||
(let ((prologue-begin (js--function-prologue-beginning)))
|
||||
(cond ((and prologue-begin (< prologue-begin (point)))
|
||||
(goto-char prologue-begin))
|
||||
(let ((prologue-begin (js--function-prologue-beginning)))
|
||||
(cond ((and prologue-begin (< prologue-begin (point)))
|
||||
(goto-char prologue-begin)
|
||||
(setq found t))
|
||||
|
||||
(js-flat-functions
|
||||
(js--beginning-of-defun-flat))
|
||||
(t
|
||||
(js--beginning-of-defun-nested))))))
|
||||
(js-flat-functions
|
||||
(setq found (js--beginning-of-defun-flat)))
|
||||
(t
|
||||
(when (js--beginning-of-defun-nested)
|
||||
(setq found t))))))
|
||||
found))
|
||||
|
||||
(defun js--flush-caches (&optional beg _ignored)
|
||||
"Flush the `js-mode' syntax cache after position BEG.
|
||||
|
@ -1489,6 +1489,7 @@ Return the name of the shell suitable for `sh-set-shell'."
|
||||
((string-match "[.]t?csh\\(rc\\)?\\>" buffer-file-name) "csh")
|
||||
((string-match "[.]zsh\\(rc\\|env\\)?\\>" buffer-file-name) "zsh")
|
||||
((equal (file-name-nondirectory buffer-file-name) ".profile") "sh")
|
||||
((equal (file-name-nondirectory buffer-file-name) "PKGBUILD") "bash")
|
||||
(t sh-shell-file)))
|
||||
|
||||
;;;###autoload
|
||||
|
25
lisp/rect.el
25
lisp/rect.el
@ -930,8 +930,9 @@ Ignores `line-move-visual'."
|
||||
(mapc #'delete-overlay (nthcdr 5 rol))
|
||||
(setcar (cdr rol) nil)))
|
||||
|
||||
(defun rectangle--duplicate-right (n)
|
||||
"Duplicate the rectangular region N times on the right-hand side."
|
||||
(defun rectangle--duplicate-right (n displacement)
|
||||
"Duplicate the rectangular region N times on the right-hand side.
|
||||
Leave the region moved DISPLACEMENT region-wide steps to the right."
|
||||
(let ((cols (rectangle--pos-cols (point) (mark))))
|
||||
(apply-on-rectangle
|
||||
(lambda (startcol endcol)
|
||||
@ -940,16 +941,22 @@ Ignores `line-move-visual'."
|
||||
(move-to-column endcol t)
|
||||
(dotimes (_ n)
|
||||
(insert (cadr lines)))))
|
||||
(region-beginning) (region-end))
|
||||
;; Recompute the rectangle state; no crutches should be needed now.
|
||||
(let ((p (point))
|
||||
(m (mark)))
|
||||
(min (point) (mark))
|
||||
(max (point) (mark)))
|
||||
;; Recompute the rectangle state.
|
||||
(let* ((p (point))
|
||||
(m (mark))
|
||||
(point-col (car cols))
|
||||
(mark-col (cdr cols))
|
||||
(d (* displacement (abs (- point-col mark-col)))))
|
||||
(rectangle--reset-crutches)
|
||||
(goto-char m)
|
||||
(move-to-column (cdr cols) t)
|
||||
(set-mark (point))
|
||||
(move-to-column (+ mark-col d) t)
|
||||
(if (= d 0)
|
||||
(set-mark (point))
|
||||
(push-mark (point)))
|
||||
(goto-char p)
|
||||
(move-to-column (car cols) t))))
|
||||
(move-to-column (+ point-col d) t))))
|
||||
|
||||
(provide 'rect)
|
||||
|
||||
|
148
lisp/register.el
148
lisp/register.el
@ -69,10 +69,12 @@ A list of the form (file . FILE-NAME) represents the file named FILE-NAME.
|
||||
A list of the form (file-query FILE-NAME POSITION) represents
|
||||
position POSITION in the file named FILE-NAME, but query before
|
||||
visiting it.
|
||||
A list of the form (buffer . BUFFER-NAME) represents the buffer BUFFER-NAME.
|
||||
A list of the form (WINDOW-CONFIGURATION POSITION)
|
||||
represents a saved window configuration plus a saved value of point.
|
||||
A list of the form (FRAME-CONFIGURATION POSITION)
|
||||
represents a saved frame configuration plus a saved value of point.")
|
||||
represents a saved frame configuration (a.k.a. \"frameset\") plus
|
||||
a saved value of point.")
|
||||
|
||||
(defgroup register nil
|
||||
"Register commands."
|
||||
@ -90,7 +92,7 @@ of the marked text."
|
||||
(character :tag "Use register" :value ?+)))
|
||||
|
||||
(defcustom register-preview-delay 1
|
||||
"If non-nil, time to wait in seconds before popping up a preview window.
|
||||
"If non-nil, time to wait in seconds before popping up register preview window.
|
||||
If nil, do not show register previews, unless `help-char' (or a member of
|
||||
`help-event-list') is pressed."
|
||||
:version "24.4"
|
||||
@ -107,7 +109,7 @@ See the documentation of the variable `register-alist' for possible VALUEs."
|
||||
(setf (alist-get register register-alist) value))
|
||||
|
||||
(defun register-describe-oneline (c)
|
||||
"One-line description of register C."
|
||||
"Return a one-line description of register C."
|
||||
(let ((d (replace-regexp-in-string
|
||||
"\n[ \t]*" " "
|
||||
(with-output-to-string (describe-register-1 c)))))
|
||||
@ -116,19 +118,19 @@ See the documentation of the variable `register-alist' for possible VALUEs."
|
||||
d)))
|
||||
|
||||
(defun register-preview-default (r)
|
||||
"Default function for the variable `register-preview-function'."
|
||||
"Function that is the default value of the variable `register-preview-function'."
|
||||
(format "%s: %s\n"
|
||||
(single-key-description (car r))
|
||||
(register-describe-oneline (car r))))
|
||||
|
||||
(defvar register-preview-function #'register-preview-default
|
||||
"Function to format a register for previewing.
|
||||
Takes one argument, a cons (NAME . CONTENTS) as found in `register-alist'.
|
||||
Returns a string.")
|
||||
Called with one argument, a cons (NAME . CONTENTS) as found in `register-alist'.
|
||||
The function should return a string, the description of teh argument.")
|
||||
|
||||
(defun register-preview (buffer &optional show-empty)
|
||||
"Pop up a window to show register preview in BUFFER.
|
||||
If SHOW-EMPTY is non-nil show the window even if no registers.
|
||||
"Pop up a window showing the registers preview in BUFFER.
|
||||
If SHOW-EMPTY is non-nil, show the window even if no registers.
|
||||
Format of each entry is controlled by the variable `register-preview-function'."
|
||||
(when (or show-empty (consp register-alist))
|
||||
(with-current-buffer-window
|
||||
@ -178,12 +180,12 @@ display such a window regardless."
|
||||
(and (get-buffer buffer) (kill-buffer buffer)))))
|
||||
|
||||
(defun point-to-register (register &optional arg)
|
||||
"Store current location of point in register REGISTER.
|
||||
With prefix argument, store current frame configuration.
|
||||
"Store current location of point in REGISTER.
|
||||
With prefix argument ARG, store current frame configuration (a.k.a. \"frameset\").
|
||||
Use \\[jump-to-register] to go to that location or restore that configuration.
|
||||
Argument is a character, naming the register.
|
||||
Argument is a character, the name of the register.
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview'."
|
||||
(interactive (list (register-read-with-preview
|
||||
(if current-prefix-arg
|
||||
"Frame configuration to register: "
|
||||
@ -196,11 +198,11 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
(point-marker))))
|
||||
|
||||
(defun window-configuration-to-register (register &optional _arg)
|
||||
"Store the window configuration of the selected frame in register REGISTER.
|
||||
"Store the window configuration of the selected frame in REGISTER.
|
||||
Use \\[jump-to-register] to restore the configuration.
|
||||
Argument is a character, naming the register.
|
||||
Argument is a character, the name of the register.
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview'."
|
||||
(interactive (list (register-read-with-preview
|
||||
"Window configuration to register: ")
|
||||
current-prefix-arg))
|
||||
@ -213,11 +215,12 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
'(register) "24.4")
|
||||
|
||||
(defun frame-configuration-to-register (register &optional _arg)
|
||||
"Store the window configuration of all frames in register REGISTER.
|
||||
"Store the window configurations of all frames in REGISTER.
|
||||
\(This window configuration is also known as \"frameset\").
|
||||
Use \\[jump-to-register] to restore the configuration.
|
||||
Argument is a character, naming the register.
|
||||
Argument is a character, the name of the register.
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview'."
|
||||
(interactive (list (register-read-with-preview
|
||||
"Frame configuration to register: ")
|
||||
current-prefix-arg))
|
||||
@ -233,18 +236,21 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
|
||||
(defalias 'register-to-point 'jump-to-register)
|
||||
(defun jump-to-register (register &optional delete)
|
||||
"Move point to location stored in a register.
|
||||
Push the mark if jumping moves point, unless called in succession.
|
||||
"Go to location stored in REGISTER, or restore configuration stored there.
|
||||
Push the mark if going to the location moves point, unless called in succession.
|
||||
If the register contains a file name, find that file.
|
||||
\(To put a file name in a register, you must use `set-register'.)
|
||||
If the register contains a buffer name, switch to that buffer.
|
||||
\(To put a file or buffer name in a register, you must use `set-register'.)
|
||||
If the register contains a window configuration (one frame) or a frameset
|
||||
\(all frames), restore that frame or all frames accordingly.
|
||||
First argument is a character, naming the register.
|
||||
Optional second arg non-nil (interactively, prefix argument) says to
|
||||
delete any existing frames that the frameset doesn't mention.
|
||||
\(Otherwise, these frames are iconified.)
|
||||
\(all frames), restore the configuration of that frame or of all frames
|
||||
accordingly.
|
||||
First argument REGISTER is a character, the name of the register.
|
||||
Optional second arg DELETE non-nil (interactively, prefix argument) says
|
||||
to delete any existing frames that the frameset doesn't mention.
|
||||
\(Otherwise, these frames are iconified.) This argument is currently
|
||||
ignored if the register contains anything but a frameset.
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview'."
|
||||
(interactive (list (register-read-with-preview "Jump to register: ")
|
||||
current-prefix-arg))
|
||||
(let ((val (get-register register)))
|
||||
@ -252,6 +258,7 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
|
||||
(cl-defgeneric register-val-jump-to (_val _arg)
|
||||
"Execute the \"jump\" operation of VAL.
|
||||
VAL is the contents of a register as returned by `get-register'.
|
||||
ARG is the value of the prefix argument or nil."
|
||||
(user-error "Register doesn't contain a buffer position or configuration"))
|
||||
|
||||
@ -301,13 +308,13 @@ ARG is the value of the prefix argument or nil."
|
||||
(marker-position (cdr elem))))))))
|
||||
|
||||
(defun number-to-register (number register)
|
||||
"Store a number in a register.
|
||||
Two args, NUMBER and REGISTER (a character, naming the register).
|
||||
If NUMBER is nil, a decimal number is read from the buffer starting
|
||||
"Store NUMBER in REGISTER.
|
||||
REGISTER is a character, the name of the register.
|
||||
If NUMBER is nil, a decimal number is read from the buffer
|
||||
at point, and point moves to the end of that number.
|
||||
Interactively, NUMBER is the prefix arg (none means nil).
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview'."
|
||||
(interactive (list current-prefix-arg
|
||||
(register-read-with-preview "Number to register: ")))
|
||||
(set-register register
|
||||
@ -320,8 +327,8 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
0))))
|
||||
|
||||
(defun increment-register (prefix register)
|
||||
"Augment contents of REGISTER.
|
||||
Interactively, PREFIX is in raw form.
|
||||
"Augment contents of REGISTER using PREFIX.
|
||||
Interactively, PREFIX is the raw prefix argument.
|
||||
|
||||
If REGISTER contains a number, add `prefix-numeric-value' of
|
||||
PREFIX to it.
|
||||
@ -329,7 +336,7 @@ PREFIX to it.
|
||||
If REGISTER is empty or if it contains text, call
|
||||
`append-to-register' with `delete-flag' set to PREFIX.
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview'."
|
||||
(interactive (list current-prefix-arg
|
||||
(register-read-with-preview "Increment register: ")))
|
||||
(let ((register-val (get-register register)))
|
||||
@ -342,10 +349,10 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
(t (user-error "Register does not contain a number or text")))))
|
||||
|
||||
(defun view-register (register)
|
||||
"Display what is contained in register named REGISTER.
|
||||
The Lisp value REGISTER is a character.
|
||||
"Display the description of the contents of REGISTER.
|
||||
REGISTER is a character, the name of the register.
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview'."
|
||||
(interactive (list (register-read-with-preview "View register: ")))
|
||||
(let ((val (get-register register)))
|
||||
(if (null val)
|
||||
@ -354,7 +361,7 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
(describe-register-1 register t)))))
|
||||
|
||||
(defun list-registers ()
|
||||
"Display a list of nonempty registers saying briefly what they contain."
|
||||
"Display the list of nonempty registers with brief descriptions of contents."
|
||||
(interactive)
|
||||
(let ((list (copy-sequence register-alist)))
|
||||
(setq list (sort list (lambda (a b) (< (car a) (car b)))))
|
||||
@ -372,7 +379,10 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
(register-val-describe val verbose)))
|
||||
|
||||
(cl-defgeneric register-val-describe (val verbose)
|
||||
"Print description of register value VAL to `standard-output'."
|
||||
"Print description of register value VAL to `standard-output'.
|
||||
Second argument VERBOSE is ignored, unless VAL is not one of the
|
||||
supported kinds of register contents, in which case it is displayed
|
||||
using `prin1'."
|
||||
(princ "Garbage:\n")
|
||||
(if verbose (prin1 val)))
|
||||
|
||||
@ -467,13 +477,14 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
(princ "the empty string")))))
|
||||
|
||||
(defun insert-register (register &optional arg)
|
||||
"Insert contents of register REGISTER. (REGISTER is a character.)
|
||||
Normally puts point before and mark after the inserted text.
|
||||
If optional second arg is non-nil, puts mark before and point after.
|
||||
Interactively, second arg is nil if prefix arg is supplied and t
|
||||
otherwise.
|
||||
"Insert contents of REGISTER at point.
|
||||
REGISTER is a character, the name of the register.
|
||||
Normally puts point before and mark after the inserted text, but
|
||||
if optional second argument ARG is non-nil, puts mark before and
|
||||
point after. Interactively, ARG is nil if prefix arg is supplied,
|
||||
and t otherwise.
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview'."
|
||||
(interactive (progn
|
||||
(barf-if-buffer-read-only)
|
||||
(list (register-read-with-preview "Insert register: ")
|
||||
@ -484,7 +495,7 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
(if (not arg) (exchange-point-and-mark)))
|
||||
|
||||
(cl-defgeneric register-val-insert (_val)
|
||||
"Insert register value VAL."
|
||||
"Insert register value VAL in current buffer at point."
|
||||
(user-error "Register does not contain text"))
|
||||
|
||||
(cl-defmethod register-val-insert ((val registerv))
|
||||
@ -507,14 +518,17 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
(cl-call-next-method val)))
|
||||
|
||||
(defun copy-to-register (register start end &optional delete-flag region)
|
||||
"Copy region into register REGISTER.
|
||||
With prefix arg, delete as well.
|
||||
Called from program, takes five args: REGISTER, START, END, DELETE-FLAG,
|
||||
"Copy region of text between START and END into REGISTER.
|
||||
If DELETE-FLAG is non-nil (interactively, prefix arg), delete the region
|
||||
after copying.
|
||||
Called from Lisp, takes five args: REGISTER, START, END, DELETE-FLAG,
|
||||
and REGION. START and END are buffer positions indicating what to copy.
|
||||
The optional argument REGION if non-nil, indicates that we're not just
|
||||
copying some text between START and END, but we're copying the region.
|
||||
The optional argument REGION, if non-nil, means START..END denotes the
|
||||
region.
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview'
|
||||
and use mark and point as START and END; REGION is always non-nil in
|
||||
this case."
|
||||
(interactive (list (register-read-with-preview "Copy to register: ")
|
||||
(region-beginning)
|
||||
(region-end)
|
||||
@ -530,12 +544,14 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
(indicate-copied-region))))
|
||||
|
||||
(defun append-to-register (register start end &optional delete-flag)
|
||||
"Append region to text in register REGISTER.
|
||||
With prefix arg, delete as well.
|
||||
Called from program, takes four args: REGISTER, START, END and DELETE-FLAG.
|
||||
"Append region of text between START and END to REGISTER.
|
||||
If DELETE-FLAG is non-nil (interactively, prefix arg), delete the region
|
||||
after appending.
|
||||
Called from Lisp, takes four args: REGISTER, START, END and DELETE-FLAG.
|
||||
START and END are buffer positions indicating what to append.
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview',
|
||||
and use mark and point as START and END."
|
||||
(interactive (list (register-read-with-preview "Append to register: ")
|
||||
(region-beginning)
|
||||
(region-end)
|
||||
@ -554,12 +570,14 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
(indicate-copied-region))))
|
||||
|
||||
(defun prepend-to-register (register start end &optional delete-flag)
|
||||
"Prepend region to text in register REGISTER.
|
||||
With prefix arg, delete as well.
|
||||
"Prepend region of text between START and END to REGISTER.
|
||||
If DELETE-FLAG is non-nil (interactively, prefix arg), delete the region
|
||||
after prepending.
|
||||
Called from program, takes four args: REGISTER, START, END and DELETE-FLAG.
|
||||
START and END are buffer positions indicating what to prepend.
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview',
|
||||
and use mark and point as START and END."
|
||||
(interactive (list (register-read-with-preview "Prepend to register: ")
|
||||
(region-beginning)
|
||||
(region-end)
|
||||
@ -578,14 +596,16 @@ Interactively, reads the register using `register-read-with-preview'."
|
||||
(indicate-copied-region))))
|
||||
|
||||
(defun copy-rectangle-to-register (register start end &optional delete-flag)
|
||||
"Copy rectangular region into register REGISTER.
|
||||
With prefix arg, delete as well.
|
||||
To insert this register in the buffer, use \\[insert-register].
|
||||
"Copy rectangular region of text between START and END into REGISTER.
|
||||
If DELETE-FLAG is non-nil (interactively, prefix arg), delete the region
|
||||
after copying.
|
||||
To insert this register into a buffer, use \\[insert-register].
|
||||
|
||||
Called from a program, takes four args: REGISTER, START, END and DELETE-FLAG.
|
||||
Called from Lisp, takes four args: REGISTER, START, END and DELETE-FLAG.
|
||||
START and END are buffer positions giving two corners of rectangle.
|
||||
|
||||
Interactively, reads the register using `register-read-with-preview'."
|
||||
Interactively, prompt for REGISTER using `register-read-with-preview',
|
||||
and use mark and point as START and END."
|
||||
(interactive (list (register-read-with-preview
|
||||
"Copy rectangle to register: ")
|
||||
(region-beginning)
|
||||
|
@ -1114,7 +1114,8 @@ parser notifying of the change."
|
||||
(when treesit--font-lock-verbose
|
||||
(message "Notifier received range: %s-%s"
|
||||
(car range) (cdr range)))
|
||||
(put-text-property (car range) (cdr range) 'fontified nil))))
|
||||
(with-silent-modifications
|
||||
(put-text-property (car range) (cdr range) 'fontified nil)))))
|
||||
|
||||
;;; Indent
|
||||
|
||||
|
@ -59,6 +59,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
#ifdef WINDOWSNT
|
||||
# include "w32common.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TREE_SITTER
|
||||
#include "treesit.h"
|
||||
#endif
|
||||
|
||||
static void update_buffer_properties (ptrdiff_t, ptrdiff_t);
|
||||
static Lisp_Object styled_format (ptrdiff_t, Lisp_Object *, bool);
|
||||
|
||||
@ -2399,6 +2404,14 @@ Both characters must have the same length of multi-byte form. */)
|
||||
if (NILP (noundo))
|
||||
record_change (pos, 1);
|
||||
for (i = 0; i < len; i++) *p++ = tostr[i];
|
||||
|
||||
#ifdef HAVE_TREE_SITTER
|
||||
/* In the previous branch, replace_range() notifies
|
||||
changes to tree-sitter, but in this branch, we
|
||||
modified buffer content manually, so we need to
|
||||
notify tree-sitter manually. */
|
||||
treesit_record_change (pos_byte, pos_byte + len, pos_byte + len);
|
||||
#endif
|
||||
}
|
||||
last_changed = pos + 1;
|
||||
}
|
||||
@ -2598,6 +2611,15 @@ It returns the number of characters changed. */)
|
||||
*p++ = *str++;
|
||||
signal_after_change (pos, 1, 1);
|
||||
update_compositions (pos, pos + 1, CHECK_BORDER);
|
||||
|
||||
#ifdef HAVE_TREE_SITTER
|
||||
/* In the previous branch, replace_range() notifies
|
||||
changes to tree-sitter, but in this branch, we
|
||||
modified buffer content manually, so we need to
|
||||
notify tree-sitter manually. */
|
||||
treesit_record_change (pos_byte, pos_byte + len,
|
||||
pos_byte + len);
|
||||
#endif
|
||||
}
|
||||
characters_changed++;
|
||||
}
|
||||
@ -4771,6 +4793,13 @@ ring. */)
|
||||
adjust_markers_bytepos (start1, start1_byte, end2, end2_byte, 0);
|
||||
}
|
||||
|
||||
#ifdef HAVE_TREE_SITTER
|
||||
/* I don't think it's common to transpose two far-apart regions, so
|
||||
amalgamating the edit into one should be fine. This is what the
|
||||
signal_after_change below does, too. */
|
||||
treesit_record_change (start1_byte, end2_byte, end2_byte);
|
||||
#endif
|
||||
|
||||
signal_after_change (start1, end2 - start1, end2 - start1);
|
||||
return Qnil;
|
||||
}
|
||||
|
120
test/lisp/erc/erc-scenarios-match.el
Normal file
120
test/lisp/erc/erc-scenarios-match.el
Normal file
@ -0,0 +1,120 @@
|
||||
;;; erc-scenarios-match.el --- Misc `erc-match' scenarios -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs 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 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; GNU Emacs 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ert-x)
|
||||
(eval-and-compile
|
||||
(let ((load-path (cons (ert-resource-directory) load-path)))
|
||||
(require 'erc-scenarios-common)))
|
||||
|
||||
(require 'erc-stamp)
|
||||
(require 'erc-match)
|
||||
|
||||
;; This defends against a regression in which all matching by the
|
||||
;; `erc-match-message' fails when `erc-add-timestamp' precedes it in
|
||||
;; `erc-insert-modify-hook'. Basically, `erc-match-message' used to
|
||||
;; expect an `erc-parsed' text property on the first character in a
|
||||
;; message, which doesn't exist, when the message content is prefixed
|
||||
;; by a leading timestamp.
|
||||
|
||||
(ert-deftest erc-scenarios-match--stamp-left-current-nick ()
|
||||
:tags '(:expensive-test)
|
||||
(erc-scenarios-common-with-cleanup
|
||||
((erc-scenarios-common-dialog "base/reconnect")
|
||||
(dumb-server (erc-d-run "localhost" t 'unexpected-disconnect))
|
||||
(port (process-contact dumb-server :service))
|
||||
(erc-server-flood-penalty 0.1)
|
||||
(erc-insert-timestamp-function 'erc-insert-timestamp-left)
|
||||
(expect (erc-d-t-make-expecter)))
|
||||
|
||||
(ert-info ("Connect")
|
||||
(with-current-buffer (erc :server "127.0.0.1"
|
||||
:port port
|
||||
:full-name "tester"
|
||||
:nick "tester")
|
||||
(should (memq 'erc-match-message
|
||||
(memq 'erc-add-timestamp erc-insert-modify-hook)))
|
||||
;; The "match type" is `current-nick'.
|
||||
(funcall expect 5 "tester")
|
||||
(should (eq (get-text-property (1- (point)) 'font-lock-face)
|
||||
'erc-current-nick-face))))))
|
||||
|
||||
;; This asserts that when stamps appear before a message,
|
||||
;; some non-nil invisibility property spans the entire message.
|
||||
(ert-deftest erc-scenarios-match--stamp-left-fools-invisible ()
|
||||
:tags '(:expensive-test)
|
||||
(erc-scenarios-common-with-cleanup
|
||||
((erc-scenarios-common-dialog "join/legacy")
|
||||
(dumb-server (erc-d-run "localhost" t 'foonet))
|
||||
(port (process-contact dumb-server :service))
|
||||
(erc-server-flood-penalty 0.1)
|
||||
(erc-insert-timestamp-function 'erc-insert-timestamp-left)
|
||||
(erc-timestamp-only-if-changed-flag nil)
|
||||
(erc-fools '("bob"))
|
||||
(erc-text-matched-hook '(erc-hide-fools))
|
||||
(erc-autojoin-channels-alist '((FooNet "#chan")))
|
||||
(expect (erc-d-t-make-expecter))
|
||||
(hiddenp (lambda ()
|
||||
(and (eq (field-at-pos (pos-bol)) 'erc-timestamp)
|
||||
(get-text-property (pos-bol) 'invisible)
|
||||
(>= (next-single-property-change (pos-bol)
|
||||
'invisible nil)
|
||||
(pos-eol))))))
|
||||
|
||||
(ert-info ("Connect")
|
||||
(with-current-buffer (erc :server "127.0.0.1"
|
||||
:port port
|
||||
:full-name "tester"
|
||||
:password "changeme"
|
||||
:nick "tester")
|
||||
(should (memq 'erc-match-message
|
||||
(memq 'erc-add-timestamp erc-insert-modify-hook)))
|
||||
(funcall expect 5 "This server is in debug mode")))
|
||||
|
||||
(ert-info ("Ensure lines featuring \"bob\" are invisible")
|
||||
(with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
|
||||
(should (funcall expect 10 "<bob> tester, welcome!"))
|
||||
(should (funcall hiddenp))
|
||||
|
||||
;; Alice's is the only one visible.
|
||||
(should (funcall expect 10 "<alice> tester, welcome!"))
|
||||
(should (eq (field-at-pos (pos-bol)) 'erc-timestamp))
|
||||
(should (get-text-property (pos-bol) 'invisible))
|
||||
(should-not (get-text-property (point) 'invisible))
|
||||
|
||||
(should (funcall expect 10 "<bob> alice: But, as it seems"))
|
||||
(should (funcall hiddenp))
|
||||
|
||||
(should (funcall expect 10 "<alice> bob: Well, this is the forest"))
|
||||
(should (funcall hiddenp))
|
||||
|
||||
(should (funcall expect 10 "<alice> bob: And will you"))
|
||||
(should (funcall hiddenp))
|
||||
|
||||
(should (funcall expect 10 "<bob> alice: Live, and be prosperous"))
|
||||
(should (funcall hiddenp))
|
||||
|
||||
(should (funcall expect 10 "ERC>"))
|
||||
(should-not (get-text-property (pos-bol) 'invisible))
|
||||
(should-not (get-text-property (point) 'invisible))))))
|
||||
|
||||
(eval-when-compile (require 'erc-join))
|
||||
|
||||
;;; erc-scenarios-match.el ends here
|
@ -143,4 +143,109 @@
|
||||
|
||||
(erc-services-mode -1)))
|
||||
|
||||
;; The server rejects your nick during registration, so ERC acquires a
|
||||
;; placeholder and successfully renicks once the connection is up.
|
||||
;; See also `erc-scenarios-base-renick-self-auto'.
|
||||
|
||||
(ert-deftest erc-scenarios-services-misc--reconnect-retry-nick ()
|
||||
:tags '(:expensive-test)
|
||||
(erc-scenarios-common-with-cleanup
|
||||
((erc-server-flood-penalty 0.1)
|
||||
(erc-scenarios-common-dialog "services/regain")
|
||||
(dumb-server (erc-d-run "localhost" t 'reconnect-retry
|
||||
'reconnect-retry-again))
|
||||
(port (process-contact dumb-server :service))
|
||||
(erc-server-auto-reconnect t)
|
||||
(erc-modules `(services-regain sasl ,@erc-modules))
|
||||
(erc-services-regain-alist
|
||||
'((Libera.Chat . erc-services-retry-nick-on-connect)))
|
||||
(expect (erc-d-t-make-expecter)))
|
||||
|
||||
;; FIXME figure out and explain why this is so.
|
||||
(should (featurep 'erc-services))
|
||||
|
||||
(ert-info ("Session succeeds but cut short")
|
||||
(with-current-buffer (erc :server "127.0.0.1"
|
||||
:port port
|
||||
:nick "tester"
|
||||
:user "tester"
|
||||
:password "changeme"
|
||||
:full-name "tester")
|
||||
(funcall expect 10 "Last login from")
|
||||
(erc-cmd-JOIN "#test")))
|
||||
|
||||
(with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#test"))
|
||||
(funcall expect 10 "was created on"))
|
||||
|
||||
(ert-info ("Service restored")
|
||||
(with-current-buffer "Libera.Chat"
|
||||
(erc-d-t-wait-for 10 erc--server-reconnect-timer)
|
||||
(funcall expect 10 "Connection failed!")
|
||||
(funcall expect 10 "already in use")
|
||||
(funcall expect 10 "changed mode for tester`")
|
||||
(funcall expect 10 "Last login from")
|
||||
(funcall expect 10 "Your new nickname is tester")))
|
||||
|
||||
(with-current-buffer (get-buffer "#test")
|
||||
(funcall expect 10 "tester ")
|
||||
(funcall expect 10 "was created on"))))
|
||||
|
||||
;; This only asserts that the handler fires and issues the right
|
||||
;; NickServ command, but it doesn't accurately recreate a
|
||||
;; disconnection, but it probably should.
|
||||
(ert-deftest erc-scenarios-services-misc--regain-command ()
|
||||
:tags '(:expensive-test)
|
||||
(erc-scenarios-common-with-cleanup
|
||||
((erc-server-flood-penalty 0.1)
|
||||
(erc-scenarios-common-dialog "services/regain")
|
||||
(dumb-server (erc-d-run "localhost" t 'taken-regain))
|
||||
(port (process-contact dumb-server :service))
|
||||
(erc-server-auto-reconnect t)
|
||||
(erc-modules `(services-regain sasl ,@erc-modules))
|
||||
(erc-services-regain-alist
|
||||
'((ExampleNet . erc-services-issue-regain)))
|
||||
(expect (erc-d-t-make-expecter)))
|
||||
|
||||
(should (featurep 'erc-services)) ; see note in prior test
|
||||
|
||||
(with-current-buffer (erc :server "127.0.0.1"
|
||||
:port port
|
||||
:nick "dummy"
|
||||
:user "tester"
|
||||
:password "changeme"
|
||||
:full-name "tester"
|
||||
:id 'ExampleNet)
|
||||
(funcall expect 10 "dummy is already in use, trying dummy`")
|
||||
(funcall expect 10 "You are now logged in as tester")
|
||||
(funcall expect 10 "-NickServ- dummy has been regained.")
|
||||
(funcall expect 10 "*** Your new nickname is dummy")
|
||||
;; Works with "given" `:id'.
|
||||
(should (and (erc-network) (not (eq (erc-network) 'ExampleNet)))))))
|
||||
|
||||
(ert-deftest erc-scenarios-services-misc--ghost-and-retry-nick ()
|
||||
:tags '(:expensive-test)
|
||||
(erc-scenarios-common-with-cleanup
|
||||
((erc-server-flood-penalty 0.1)
|
||||
(erc-scenarios-common-dialog "services/regain")
|
||||
(dumb-server (erc-d-run "localhost" t 'taken-ghost))
|
||||
(port (process-contact dumb-server :service))
|
||||
(erc-server-auto-reconnect t)
|
||||
(erc-modules `(services-regain sasl ,@erc-modules))
|
||||
(erc-services-regain-alist
|
||||
'((FooNet . erc-services-issue-ghost-and-retry-nick)))
|
||||
(expect (erc-d-t-make-expecter)))
|
||||
|
||||
(should (featurep 'erc-services)) ; see note in prior test
|
||||
|
||||
(with-current-buffer (erc :server "127.0.0.1"
|
||||
:port port
|
||||
:nick "dummy"
|
||||
:user "tester"
|
||||
:password "changeme"
|
||||
:full-name "tester")
|
||||
(funcall expect 10 "dummy is already in use, trying dummy`")
|
||||
(funcall expect 10 "You are now logged in as tester")
|
||||
(funcall expect 10 "-NickServ- dummy has been ghosted.")
|
||||
(funcall expect 10 "*** Your new nickname is dummy"))))
|
||||
|
||||
;;; erc-scenarios-services-misc.el ends here
|
||||
|
@ -0,0 +1,56 @@
|
||||
;; -*- mode: lisp-data; -*-
|
||||
((cap 10 "CAP REQ :sasl"))
|
||||
((nick 10 "NICK tester"))
|
||||
((user 10 "USER tester 0 * :tester"))
|
||||
|
||||
((authenticate 10 "AUTHENTICATE PLAIN")
|
||||
(0.04 ":tantalum.libera.chat NOTICE * :*** Checking Ident")
|
||||
(0.01 ":tantalum.libera.chat NOTICE * :*** Looking up your hostname...")
|
||||
(0.01 ":tantalum.libera.chat NOTICE * :*** Couldn't look up your hostname")
|
||||
(0.06 ":tantalum.libera.chat NOTICE * :*** No Ident response")
|
||||
(0.02 ":tantalum.libera.chat CAP * ACK :sasl")
|
||||
(0.03 ":tantalum.libera.chat 433 * tester :Nickname is already in use."))
|
||||
|
||||
((nick 10 "NICK tester`")
|
||||
(0.03 "AUTHENTICATE +"))
|
||||
|
||||
((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==")
|
||||
(0.06 ":tantalum.libera.chat 900 tester` tester`!tester@127.0.0.1 tester :You are now logged in as tester")
|
||||
(0.02 ":tantalum.libera.chat 903 tester` :SASL authentication successful"))
|
||||
|
||||
((cap 10 "CAP END")
|
||||
(0.02 ":tantalum.libera.chat 001 tester` :Welcome to the Libera.Chat Internet Relay Chat Network tester`")
|
||||
(0.02 ":tantalum.libera.chat 002 tester` :Your host is tantalum.libera.chat[93.158.237.2/6697], running version solanum-1.0-dev")
|
||||
(0.02 ":tantalum.libera.chat 003 tester` :This server was created Mon Feb 13 2023 at 12:05:04 UTC")
|
||||
(0.01 ":tantalum.libera.chat 004 tester` tantalum.libera.chat solanum-1.0-dev DGMQRSZaghilopsuwz CFILMPQRSTbcefgijklmnopqrstuvz bkloveqjfI")
|
||||
(0.01 ":tantalum.libera.chat 005 tester` WHOX MONITOR=100 SAFELIST ELIST=CMNTU ETRACE FNC CALLERID=g KNOCK CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQRSTcgimnprstuz :are supported by this server")
|
||||
(0.01 ":tantalum.libera.chat 005 tester` CHANLIMIT=#:250 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=Libera.Chat STATUSMSG=@+ CASEMAPPING=rfc1459 NICKLEN=16 MAXNICKLEN=16 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server")
|
||||
(0.03 ":tantalum.libera.chat 005 tester` TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,ajrxz :are supported by this server")
|
||||
(0.01 ":tantalum.libera.chat 251 tester` :There are 70 users and 42977 invisible on 28 servers")
|
||||
(0.00 ":tantalum.libera.chat 252 tester` 38 :IRC Operators online")
|
||||
(0.00 ":tantalum.libera.chat 253 tester` 87 :unknown connection(s)")
|
||||
(0.00 ":tantalum.libera.chat 254 tester` 22908 :channels formed")
|
||||
(0.00 ":tantalum.libera.chat 255 tester` :I have 2507 clients and 1 servers")
|
||||
(0.00 ":tantalum.libera.chat 265 tester` 2507 3232 :Current local users 2507, max 3232")
|
||||
(0.00 ":tantalum.libera.chat 266 tester` 43047 51777 :Current global users 43047, max 51777")
|
||||
(0.00 ":tantalum.libera.chat 250 tester` :Highest connection count: 3233 (3232 clients) (284887 connections received)")
|
||||
(0.03 ":tantalum.libera.chat 375 tester` :- tantalum.libera.chat Message of the Day - ")
|
||||
(0.00 ":tantalum.libera.chat 372 tester` :- This server provided by Hyperfilter (https://hyperfilter.com)")
|
||||
(0.00 ":tantalum.libera.chat 372 tester` :- Email: support@libera.chat")
|
||||
(0.02 ":tantalum.libera.chat 376 tester` :End of /MOTD command."))
|
||||
|
||||
((mode 10 "MODE tester` +i")
|
||||
(0.01 ":tester` MODE tester` :+Ziw")
|
||||
(0.02 ":SaslServ!SaslServ@services.libera.chat NOTICE tester` :Last login from: \2~tester@127.0.0.1\2 on Apr 07 01:36:25 2023 +0000."))
|
||||
|
||||
((nick 10 "NICK tester")
|
||||
(0.02 ":tester`!~tester@127.0.0.1 NICK :tester"))
|
||||
|
||||
((join 10 "JOIN #test")
|
||||
(0.02 ":tester!~tester@127.0.0.1 JOIN #test")
|
||||
(0.02 ":tantalum.libera.chat 353 tester = #test :tester zbyqbepbqre7 pusevgfpu Thrfg2187 zngbeb qnexNssvavgl wrebzr- rqpentt Ilehf grfg2 AvtugZbaxrl pevfgvvbna xrivap_ fnvybePng shohxv gxan arrqyr avpx16 NeanhqW_kzcc jvyyr wrnaogeq Wnarg cnefavc0 Xbentt RcvpArb flfqrs wfgbxre hafcrag__ Lbevpx_")
|
||||
(0.02 ":tantalum.libera.chat 366 tester #test :End of /NAMES list."))
|
||||
|
||||
((mode 10 "MODE #test")
|
||||
(0.02 ":tantalum.libera.chat 324 tester #test +nt")
|
||||
(0.02 ":tantalum.libera.chat 329 tester #test 1621432263"))
|
53
test/lisp/erc/resources/services/regain/reconnect-retry.eld
Normal file
53
test/lisp/erc/resources/services/regain/reconnect-retry.eld
Normal file
@ -0,0 +1,53 @@
|
||||
;; -*- mode: lisp-data; -*-
|
||||
((cap 10 "CAP REQ :sasl"))
|
||||
((nick 10 "NICK tester"))
|
||||
((user 10 "USER tester 0 * :tester"))
|
||||
|
||||
((authenticate 10 "AUTHENTICATE PLAIN")
|
||||
(0.02 ":cadmium.libera.chat NOTICE * :*** Checking Ident")
|
||||
(0.01 ":cadmium.libera.chat NOTICE * :*** Looking up your hostname...")
|
||||
(0.01 ":cadmium.libera.chat NOTICE * :*** Couldn't look up your hostname")
|
||||
(0.06 ":cadmium.libera.chat NOTICE * :*** No Ident response")
|
||||
(0.09 ":cadmium.libera.chat CAP * ACK :sasl")
|
||||
(0.01 "AUTHENTICATE +"))
|
||||
|
||||
((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==")
|
||||
(0.03 ":cadmium.libera.chat 900 tester tester!tester@127.0.0.1 tester :You are now logged in as tester")
|
||||
(0.01 ":cadmium.libera.chat 903 tester :SASL authentication successful"))
|
||||
|
||||
((cap 10 "CAP END")
|
||||
(0.03 ":cadmium.libera.chat 001 tester :Welcome to the Libera.Chat Internet Relay Chat Network tester")
|
||||
(0.02 ":cadmium.libera.chat 002 tester :Your host is cadmium.libera.chat[103.196.37.95/6697], running version solanum-1.0-dev")
|
||||
(0.01 ":cadmium.libera.chat 003 tester :This server was created Wed Jan 25 2023 at 10:22:45 UTC")
|
||||
(0.01 ":cadmium.libera.chat 004 tester cadmium.libera.chat solanum-1.0-dev DGMQRSZaghilopsuwz CFILMPQRSTbcefgijklmnopqrstuvz bkloveqjfI")
|
||||
(0.00 ":cadmium.libera.chat 005 tester CALLERID=g WHOX ETRACE FNC SAFELIST ELIST=CMNTU KNOCK MONITOR=100 CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQRSTcgimnprstuz :are supported by this server")
|
||||
(0.01 ":cadmium.libera.chat 005 tester CHANLIMIT=#:250 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=Libera.Chat STATUSMSG=@+ CASEMAPPING=rfc1459 NICKLEN=16 MAXNICKLEN=16 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server")
|
||||
(0.01 ":cadmium.libera.chat 005 tester TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,ajrxz :are supported by this server")
|
||||
(0.01 ":cadmium.libera.chat 251 tester :There are 70 users and 42996 invisible on 28 servers")
|
||||
(0.02 ":cadmium.libera.chat 252 tester 38 :IRC Operators online")
|
||||
(0.01 ":cadmium.libera.chat 253 tester 57 :unknown connection(s)")
|
||||
(0.01 ":cadmium.libera.chat 254 tester 22912 :channels formed")
|
||||
(0.01 ":cadmium.libera.chat 255 tester :I have 2499 clients and 1 servers")
|
||||
(0.01 ":cadmium.libera.chat 265 tester 2499 4187 :Current local users 2499, max 4187")
|
||||
(0.01 ":cadmium.libera.chat 266 tester 43066 51827 :Current global users 43066, max 51827")
|
||||
(0.01 ":cadmium.libera.chat 250 tester :Highest connection count: 4188 (4187 clients) (319420 connections received)")
|
||||
(0.01 ":cadmium.libera.chat 375 tester :- cadmium.libera.chat Message of the Day - ")
|
||||
(0.01 ":cadmium.libera.chat 372 tester :- This server kindly provided by Mach Dilemma (www.m-d.net)")
|
||||
(0.01 ":cadmium.libera.chat 372 tester :- Welcome to Libera Chat, the IRC network for")
|
||||
(0.00 ":cadmium.libera.chat 372 tester :- Email: support@libera.chat")
|
||||
(0.00 ":cadmium.libera.chat 376 tester :End of /MOTD command.")
|
||||
(0.00 ":tester MODE tester :+Ziw")
|
||||
(0.02 ":SaslServ!SaslServ@services.libera.chat NOTICE tester :Last login from: \2~tester@127.0.0.1\2 on Apr 07 01:02:11 2023 +0000."))
|
||||
|
||||
((mode 10 "MODE tester +i"))
|
||||
|
||||
((join 10 "JOIN #test")
|
||||
(0.09 ":tester!~tester@127.0.0.1 JOIN #test"))
|
||||
|
||||
((mode 10 "MODE #test")
|
||||
(0.03 ":cadmium.libera.chat 353 tester = #test :tester zbyqbepbqre7 pusevgfpu Thrfg2187 zngbeb qnexNssvavgl wrebzr- rqpentt Ilehf grfg2 AvtugZbaxrl pevfgvvbna xrivap_ fnvybePng shohxv gxan arrqyr avpx16 NeanhqW_kzcc Lbevpx_ hafcrag__ wfgbxre flfqrs RcvpArb Xbentt jvyyr cnefavc0 Wnarg wrnaogeq")
|
||||
(0.02 ":cadmium.libera.chat 366 tester #test :End of /NAMES list.")
|
||||
(0.00 ":cadmium.libera.chat 324 tester #test +nt")
|
||||
(0.01 ":cadmium.libera.chat 329 tester #test 1621432263"))
|
||||
|
||||
((drop 0 DROP))
|
42
test/lisp/erc/resources/services/regain/taken-ghost.eld
Normal file
42
test/lisp/erc/resources/services/regain/taken-ghost.eld
Normal file
@ -0,0 +1,42 @@
|
||||
;; -*- mode: lisp-data; -*-
|
||||
((cap 10 "CAP REQ :sasl")
|
||||
(0.00 ":irc.example.net NOTICE * :*** Looking up your hostname...")
|
||||
(0.01 ":irc.example.net NOTICE * :*** Could not resolve your hostname: Domain not found; using your IP address (10.0.2.100) instead."))
|
||||
((nick 10 "NICK dummy"))
|
||||
((user 10 "USER dummy 0 * :tester"))
|
||||
((authenticate 10 "AUTHENTICATE PLAIN")
|
||||
(0.00 ":irc.example.net CAP * ACK :sasl")
|
||||
(0.03 ":irc.example.net 433 * dummy :Nickname is already in use.")
|
||||
(0.04 "AUTHENTICATE :+"))
|
||||
((nick 10 "NICK dummy`")
|
||||
(0.00 "PING :orrMOjk^|V"))
|
||||
((~pong 10 "PONG :orrMOjk^|V"))
|
||||
((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==")
|
||||
(0.01 ":irc.example.net 900 dummy` dummy`!dummy@10.0.2.100 tester :You are now logged in as tester")
|
||||
(0.01 ":irc.example.net 903 dummy` :SASL authentication successful"))
|
||||
((cap 10 "CAP END")
|
||||
(0.00 ":irc.example.net 001 dummy` :Welcome to the FooNet IRC Network dummy`!dummy@10.0.2.100")
|
||||
(0.03 ":irc.example.net 002 dummy` :Your host is irc.example.net, running version InspIRCd-3")
|
||||
(0.01 ":irc.example.net 003 dummy` :This server was created 13:01:55 Jun 08 2023")
|
||||
(0.01 ":irc.example.net 004 dummy` irc.example.net InspIRCd-3 BIRcgikorsw ACHIKMORTXabcefghijklmnopqrstvz :HIXabefghjkloqv")
|
||||
(0.00 ":irc.example.net 005 dummy` ACCEPT=30 AWAYLEN=200 BOT=B CALLERID=g CASEMAPPING=ascii CHANLIMIT=#:20 CHANMODES=IXbeg,k,Hfjl,ACKMORTcimnprstz CHANNELLEN=64 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e :are supported by this server")
|
||||
(0.01 ":irc.example.net 005 dummy` EXTBAN=,ACORTUacjrwz HOSTLEN=64 INVEX=I KEYLEN=32 KICKLEN=255 LINELEN=512 MAXLIST=I:100,X:100,b:100,e:100,g:100 MAXTARGETS=20 MODES=20 MONITOR=30 NAMELEN=128 NAMESX NETWORK=FooNet :are supported by this server")
|
||||
(0.01 ":irc.example.net 005 dummy` NICKLEN=30 PREFIX=(qaohv)~&@%+ SAFELIST SILENCE=32 STATUSMSG=~&@%+ TOPICLEN=307 UHNAMES USERIP USERLEN=10 USERMODES=,,s,BIRcgikorw WHOX :are supported by this server")
|
||||
(0.01 ":irc.example.net 251 dummy` :There are 2 users and 1 invisible on 2 servers")
|
||||
(0.01 ":irc.example.net 253 dummy` 1 :unknown connections")
|
||||
(0.00 ":irc.example.net 254 dummy` 1 :channels formed")
|
||||
(0.00 ":irc.example.net 255 dummy` :I have 3 clients and 1 servers")
|
||||
(0.00 ":irc.example.net 265 dummy` :Current local users: 3 Max: 4")
|
||||
(0.00 ":irc.example.net 266 dummy` :Current global users: 3 Max: 4")
|
||||
(0.00 ":irc.example.net 375 dummy` :irc.example.net message of the day")
|
||||
(0.00 ":irc.example.net 372 dummy` : Have fun with the image!")
|
||||
(0.00 ":irc.example.net 376 dummy` :End of message of the day."))
|
||||
|
||||
((mode 10 "MODE dummy` +i"))
|
||||
((privmsg 10 "PRIVMSG NickServ :GHOST dummy")
|
||||
(0.00 ":irc.example.net 501 dummy` x :is not a recognised user mode.")
|
||||
(0.00 ":irc.example.net NOTICE dummy` :*** You are connected to irc.example.net using TLS (SSL) cipher 'TLS1.3-ECDHE-RSA-AES-256-GCM-AEAD'")
|
||||
(0.03 ":dummy`!dummy@10.0.2.100 MODE dummy` :+i")
|
||||
(0.02 ":NickServ!NickServ@services.int NOTICE dummy` :\2dummy\2 has been ghosted."))
|
||||
((nick 10 "NICK dummy")
|
||||
(0.02 ":dummy`!dummy@10.0.2.100 NICK :dummy"))
|
42
test/lisp/erc/resources/services/regain/taken-regain.eld
Normal file
42
test/lisp/erc/resources/services/regain/taken-regain.eld
Normal file
@ -0,0 +1,42 @@
|
||||
;; -*- mode: lisp-data; -*-
|
||||
((cap 10 "CAP REQ :sasl")
|
||||
(0.00 ":irc.example.net NOTICE * :*** Looking up your hostname...")
|
||||
(0.01 ":irc.example.net NOTICE * :*** Could not resolve your hostname: Domain not found; using your IP address (10.0.2.100) instead."))
|
||||
((nick 10 "NICK dummy"))
|
||||
((user 10 "USER dummy 0 * :tester"))
|
||||
;; This also happens to a test late ACK (see ghost variant for server-sent PING)
|
||||
((authenticate 10 "AUTHENTICATE PLAIN")
|
||||
(0.00 ":irc.example.net CAP * ACK :sasl")
|
||||
(0.09 ":irc.example.net 433 * dummy :Nickname is already in use.")
|
||||
(0.04 "AUTHENTICATE :+"))
|
||||
((nick 10 "NICK dummy`"))
|
||||
((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==")
|
||||
(0.00 ":irc.example.net 900 dummy` dummy`!dummy@10.0.2.100 tester :You are now logged in as tester")
|
||||
(0.01 ":irc.example.net 903 dummy` :SASL authentication successful"))
|
||||
|
||||
((cap 10 "CAP END")
|
||||
(0.00 ":irc.example.net 001 dummy` :Welcome to the FooNet IRC Network dummy`!dummy@10.0.2.100")
|
||||
(0.02 ":irc.example.net 002 dummy` :Your host is irc.example.net, running version InspIRCd-3")
|
||||
(0.02 ":irc.example.net 003 dummy` :This server was created 08:16:52 Jun 08 2023")
|
||||
(0.01 ":irc.example.net 004 dummy` irc.example.net InspIRCd-3 BIRcgikorsw ACHIKMORTXabcefghijklmnopqrstvz :HIXabefghjkloqv")
|
||||
(0.00 ":irc.example.net 005 dummy` ACCEPT=30 AWAYLEN=200 BOT=B CALLERID=g CASEMAPPING=ascii CHANLIMIT=#:20 CHANMODES=IXbeg,k,Hfjl,ACKMORTcimnprstz CHANNELLEN=64 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e :are supported by this server")
|
||||
(0.01 ":irc.example.net 005 dummy` EXTBAN=,ACORTUacjrwz HOSTLEN=64 INVEX=I KEYLEN=32 KICKLEN=255 LINELEN=512 MAXLIST=I:100,X:100,b:100,e:100,g:100 MAXTARGETS=20 MODES=20 MONITOR=30 NAMELEN=128 NAMESX NETWORK=FooNet :are supported by this server")
|
||||
(0.01 ":irc.example.net 005 dummy` NICKLEN=30 PREFIX=(qaohv)~&@%+ SAFELIST SILENCE=32 STATUSMSG=~&@%+ TOPICLEN=307 UHNAMES USERIP USERLEN=10 USERMODES=,,s,BIRcgikorw WHOX :are supported by this server")
|
||||
(0.01 ":irc.example.net 251 dummy` :There are 2 users and 1 invisible on 2 servers")
|
||||
(0.01 ":irc.example.net 253 dummy` 1 :unknown connections")
|
||||
(0.00 ":irc.example.net 254 dummy` 1 :channels formed")
|
||||
(0.02 ":irc.example.net 255 dummy` :I have 3 clients and 1 servers")
|
||||
(0.00 ":irc.example.net 265 dummy` :Current local users: 3 Max: 4")
|
||||
(0.00 ":irc.example.net 266 dummy` :Current global users: 3 Max: 4")
|
||||
(0.00 ":irc.example.net 375 dummy` :irc.example.net message of the day")
|
||||
(0.00 ":irc.example.net 372 dummy` : Have fun with the image!")
|
||||
(0.00 ":irc.example.net 376 dummy` :End of message of the day.")
|
||||
(0.00 ":irc.example.net 501 dummy` x :is not a recognised user mode.")
|
||||
(0.00 ":irc.example.net NOTICE dummy` :*** You are connected to irc.example.net using TLS (SSL) cipher 'TLS1.3-ECDHE-RSA-AES-256-GCM-AEAD'"))
|
||||
|
||||
((mode 10 "MODE dummy` +i"))
|
||||
|
||||
((privmsg 10 "PRIVMSG NickServ :REGAIN dummy")
|
||||
(0.00 ":dummy`!dummy@10.0.2.100 MODE dummy` :+i")
|
||||
(0.02 ":NickServ!NickServ@services.int NOTICE dummy` :\2dummy\2 has been regained.")
|
||||
(0.02 ":dummy`!dummy@10.0.2.100 NICK :dummy"))
|
@ -24,6 +24,7 @@
|
||||
;;; Code:
|
||||
|
||||
(require 'ert)
|
||||
(require 'misc)
|
||||
|
||||
(defmacro with-misc-test (original result &rest body)
|
||||
(declare (indent 2))
|
||||
@ -113,40 +114,70 @@
|
||||
(require 'rect)
|
||||
|
||||
(ert-deftest misc--duplicate-dwim ()
|
||||
;; Duplicate a line.
|
||||
(with-temp-buffer
|
||||
(insert "abc\ndefg\nh\n")
|
||||
(goto-char 7)
|
||||
(duplicate-dwim 2)
|
||||
(should (equal (buffer-string) "abc\ndefg\ndefg\ndefg\nh\n"))
|
||||
(should (equal (point) 7)))
|
||||
(let ((duplicate-line-final-position 0)
|
||||
(duplicate-region-final-position 0))
|
||||
;; Duplicate a line.
|
||||
(dolist (final-pos '(0 -1 1))
|
||||
(ert-info ((prin1-to-string final-pos) :prefix "final-pos: ")
|
||||
(with-temp-buffer
|
||||
(insert "abc\ndefg\nh\n")
|
||||
(goto-char 7)
|
||||
(let ((duplicate-line-final-position final-pos))
|
||||
(duplicate-dwim 3))
|
||||
(should (equal (buffer-string) "abc\ndefg\ndefg\ndefg\ndefg\nh\n"))
|
||||
(let ((delta (* 5 (if (< final-pos 0) 3 final-pos))))
|
||||
(should (equal (point) (+ 7 delta)))))))
|
||||
|
||||
;; Duplicate a region.
|
||||
(with-temp-buffer
|
||||
(insert "abc\ndef\n")
|
||||
(set-mark 2)
|
||||
(goto-char 7)
|
||||
(transient-mark-mode)
|
||||
(should (use-region-p))
|
||||
(duplicate-dwim)
|
||||
(should (equal (buffer-string) "abc\ndebc\ndef\n"))
|
||||
(should (equal (point) 7))
|
||||
(should (region-active-p))
|
||||
(should (equal (mark) 2)))
|
||||
;; Duplicate a region.
|
||||
(dolist (final-pos '(0 -1 1))
|
||||
(ert-info ((prin1-to-string final-pos) :prefix "final-pos: ")
|
||||
(with-temp-buffer
|
||||
(insert "abCDEFghi")
|
||||
(set-mark 3)
|
||||
(goto-char 7)
|
||||
(transient-mark-mode)
|
||||
(should (use-region-p))
|
||||
(let ((duplicate-region-final-position final-pos))
|
||||
(duplicate-dwim 3))
|
||||
(should (equal (buffer-string) "abCDEFCDEFCDEFCDEFghi"))
|
||||
(should (region-active-p))
|
||||
(let ((delta (* 4 (if (< final-pos 0) 3 final-pos))))
|
||||
(should (equal (point) (+ 7 delta)))
|
||||
(should (equal (mark) (+ 3 delta)))))))
|
||||
|
||||
;; Duplicate a rectangular region (sparse).
|
||||
(with-temp-buffer
|
||||
(insert "x\n>a\n>bcde\n>fg\nyz\n")
|
||||
(goto-char 4)
|
||||
(rectangle-mark-mode)
|
||||
(goto-char 15)
|
||||
(rectangle-forward-char 1)
|
||||
(duplicate-dwim)
|
||||
(should (equal (buffer-string) "x\n>a a \n>bcdbcde\n>fg fg \nyz\n"))
|
||||
(should (equal (point) 24))
|
||||
(should (region-active-p))
|
||||
(should rectangle-mark-mode)
|
||||
(should (equal (mark) 4)))
|
||||
|
||||
;; Idem (dense).
|
||||
(dolist (final-pos '(0 -1 1))
|
||||
(ert-info ((prin1-to-string final-pos) :prefix "final-pos: ")
|
||||
(with-temp-buffer
|
||||
(insert "aBCd\neFGh\niJKl\n")
|
||||
(goto-char 2)
|
||||
(rectangle-mark-mode)
|
||||
(goto-char 14)
|
||||
(let ((duplicate-region-final-position final-pos))
|
||||
(duplicate-dwim 3))
|
||||
(should (equal (buffer-string)
|
||||
"aBCBCBCBCd\neFGFGFGFGh\niJKJKJKJKl\n"))
|
||||
(should (region-active-p))
|
||||
(should rectangle-mark-mode)
|
||||
(let ((hdelta (* 2 (if (< final-pos 0) 3 final-pos)))
|
||||
(vdelta 12))
|
||||
(should (equal (point) (+ 14 vdelta hdelta)))
|
||||
(should (equal (mark) (+ 2 hdelta)))))))))
|
||||
|
||||
;; Duplicate a rectangular region.
|
||||
(with-temp-buffer
|
||||
(insert "x\n>a\n>bcde\n>fg\nyz\n")
|
||||
(goto-char 4)
|
||||
(rectangle-mark-mode)
|
||||
(goto-char 15)
|
||||
(rectangle-forward-char 1)
|
||||
(duplicate-dwim)
|
||||
(should (equal (buffer-string) "x\n>a a \n>bcdbcde\n>fg fg \nyz\n"))
|
||||
(should (equal (point) 24))
|
||||
(should (region-active-p))
|
||||
(should rectangle-mark-mode)
|
||||
(should (equal (mark) 4))))
|
||||
|
||||
(provide 'misc-tests)
|
||||
;;; misc-tests.el ends here
|
||||
|
50
test/lisp/progmodes/cperl-mode-resources/cperl-bug-11733.pl
Normal file
50
test/lisp/progmodes/cperl-mode-resources/cperl-bug-11733.pl
Normal file
@ -0,0 +1,50 @@
|
||||
# This resource file can be run with cperl--run-testcases from
|
||||
# cperl-tests.el and works with both perl-mode and cperl-mode.
|
||||
|
||||
# -------- Multiline declaration: input -------
|
||||
#!/usr/bin/env perl
|
||||
# -*- mode: cperl -*-
|
||||
|
||||
sub foo
|
||||
{
|
||||
}
|
||||
|
||||
sub bar
|
||||
{
|
||||
}
|
||||
# -------- Multiline declaration: expected output -------
|
||||
#!/usr/bin/env perl
|
||||
# -*- mode: cperl -*-
|
||||
|
||||
sub foo
|
||||
{
|
||||
}
|
||||
|
||||
sub bar
|
||||
{
|
||||
}
|
||||
# -------- Multiline declaration: end -------
|
||||
|
||||
# -------- Fred Colon at work: input --------
|
||||
#!/usr/bin/env perl
|
||||
# -*- mode: cperl -*-
|
||||
|
||||
while (<>)
|
||||
{
|
||||
m:^ \d+ p:
|
||||
or die;
|
||||
m:^ \d+ :
|
||||
or die;
|
||||
}
|
||||
# -------- Fred Colon at work: expected output --------
|
||||
#!/usr/bin/env perl
|
||||
# -*- mode: cperl -*-
|
||||
|
||||
while (<>)
|
||||
{
|
||||
m:^ \d+ p:
|
||||
or die;
|
||||
m:^ \d+ :
|
||||
or die;
|
||||
}
|
||||
# -------- Fred Colon at work: end --------
|
@ -24,3 +24,32 @@ package P {
|
||||
}
|
||||
}
|
||||
# -------- Bug#64364: end -------
|
||||
|
||||
# Now do this with multiline initializers
|
||||
# -------- signature with init: input -------
|
||||
package P {
|
||||
sub way { ...; }
|
||||
# perl 5.38 or newer
|
||||
sub bus
|
||||
:lvalue
|
||||
($sig,
|
||||
$na //= 42,
|
||||
@ture)
|
||||
{
|
||||
...;
|
||||
}
|
||||
}
|
||||
# -------- signature with init: expected output -------
|
||||
package P {
|
||||
sub way { ...; }
|
||||
# perl 5.38 or newer
|
||||
sub bus
|
||||
:lvalue
|
||||
($sig,
|
||||
$na //= 42,
|
||||
@ture)
|
||||
{
|
||||
...;
|
||||
}
|
||||
}
|
||||
# -------- signature with init: end -------
|
||||
|
@ -34,9 +34,17 @@
|
||||
# A signature with a trailing comma (weird, but legal)
|
||||
sub sub_5 ($foo,$bar,) { ...; }
|
||||
|
||||
# Perl 5.38-style initializer
|
||||
sub sub_6
|
||||
($foo,
|
||||
$bar //= "baz")
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
# Part 2: Same constructs for anonymous subs
|
||||
# A plain named subroutine without any optional stuff
|
||||
my $subref_0 = sub { ...; }
|
||||
my $subref_0 = sub { ...; };
|
||||
|
||||
# A prototype and a trivial subroutine attribute
|
||||
{
|
||||
|
@ -184,11 +184,12 @@ attributes, prototypes and signatures."
|
||||
(when (match-beginning 2)
|
||||
(should (equal (get-text-property (match-beginning 2) 'face)
|
||||
'font-lock-string-face))))
|
||||
(goto-char end-of-sub)
|
||||
;; Subroutine signatures
|
||||
(goto-char start-of-sub)
|
||||
(when (search-forward "$bar" end-of-sub t)
|
||||
(should (equal (get-text-property (match-beginning) 'face)
|
||||
'font-lock-variable-name-face)))))
|
||||
(should (equal (get-text-property (match-beginning 0) 'face)
|
||||
'font-lock-variable-name-face)))
|
||||
(goto-char end-of-sub)))
|
||||
;; Anonymous subroutines
|
||||
(while (search-forward-regexp "= sub" nil t)
|
||||
(let ((start-of-sub (match-beginning 0))
|
||||
@ -205,11 +206,12 @@ attributes, prototypes and signatures."
|
||||
(when (match-beginning 2)
|
||||
(should (equal (get-text-property (match-beginning 2) 'face)
|
||||
'font-lock-string-face))))
|
||||
(goto-char end-of-sub)
|
||||
;; Subroutine signatures
|
||||
(goto-char start-of-sub)
|
||||
(when (search-forward "$bar" end-of-sub t)
|
||||
(should (equal (get-text-property (match-beginning) 'face)
|
||||
'font-lock-variable-name-face))))))))
|
||||
(should (equal (get-text-property (match-beginning 0) 'face)
|
||||
'font-lock-variable-name-face)))
|
||||
(goto-char end-of-sub))))))
|
||||
|
||||
(ert-deftest cperl-test-fontify-special-variables ()
|
||||
"Test fontification of variables like $^T or ${^ENCODING}.
|
||||
@ -314,6 +316,7 @@ issued by CPerl mode."
|
||||
|
||||
(defvar perl-continued-statement-offset)
|
||||
(defvar perl-indent-level)
|
||||
(defvar perl-indent-parens-as-block)
|
||||
|
||||
(defconst cperl--tests-heredoc-face
|
||||
(if (equal cperl-test-mode 'perl-mode) 'perl-heredoc
|
||||
@ -852,6 +855,17 @@ under timeout control."
|
||||
(should (string-match
|
||||
"poop ('foo', \n 'bar')" (buffer-string))))))
|
||||
|
||||
(ert-deftest cperl-test-bug-11733 ()
|
||||
"Verify indentation of braces after newline and non-labels."
|
||||
(skip-unless (eq cperl-test-mode #'cperl-mode))
|
||||
(cperl--run-test-cases
|
||||
(ert-resource-file "cperl-bug-11733.pl")
|
||||
(goto-char (point-min))
|
||||
(while (null (eobp))
|
||||
(cperl-indent-command)
|
||||
(forward-line 1))))
|
||||
|
||||
|
||||
(ert-deftest cperl-test-bug-11996 ()
|
||||
"Verify that we give the right syntax property to a backslash operator."
|
||||
(with-temp-buffer
|
||||
|
@ -237,6 +237,57 @@ if (!/[ (:,='\"]/.test(value)) {
|
||||
(js-deftest-indent "jsx-unclosed-2.jsx")
|
||||
(js-deftest-indent "jsx.jsx")
|
||||
|
||||
;;;; Navigation tests.
|
||||
|
||||
(ert-deftest js-mode-beginning-of-defun ()
|
||||
(with-temp-buffer
|
||||
(insert "function foo() {
|
||||
var value = 1;
|
||||
}
|
||||
|
||||
/** A comment. */
|
||||
function bar() {
|
||||
var value = 1;
|
||||
}
|
||||
")
|
||||
(js-mode)
|
||||
;; Move point inside `foo'.
|
||||
(goto-char 18)
|
||||
(beginning-of-defun)
|
||||
(should (bobp))
|
||||
;; Move point between the two functions.
|
||||
(goto-char 37)
|
||||
(beginning-of-defun)
|
||||
(should (bobp))
|
||||
;; Move point inside `bar'.
|
||||
(goto-char 73)
|
||||
(beginning-of-defun)
|
||||
;; Point should move to the beginning of `bar'.
|
||||
(should (equal (point) 56))))
|
||||
|
||||
(ert-deftest js-mode-end-of-defun ()
|
||||
(with-temp-buffer
|
||||
(insert "function foo() {
|
||||
var value = 1;
|
||||
}
|
||||
|
||||
/** A comment. */
|
||||
function bar() {
|
||||
var value = 1;
|
||||
}
|
||||
")
|
||||
(js-mode)
|
||||
(goto-char (point-min))
|
||||
(end-of-defun)
|
||||
;; end-of-defun from the beginning of the buffer should go to the
|
||||
;; end of `foo'.
|
||||
(should (equal (point) 37))
|
||||
;; Move point to the beginning of /** A comment. */
|
||||
(goto-char 38)
|
||||
(end-of-defun)
|
||||
;; end-of-defun should move point to eob.
|
||||
(should (eobp))))
|
||||
|
||||
(provide 'js-tests)
|
||||
|
||||
;;; js-tests.el ends here
|
||||
|
Loading…
Reference in New Issue
Block a user