Spracherkennung für: .g vermutete Sprache: Python {Python[81] SQL[73] VHDL[65]} [Methode: maximale Elemente, drei Dimensionen]
#############################################################################
##
## This file is part of GAP, a system for computational discrete algebra.
## This file's authors include Frank Lübeck.
##
## Copyright of GAP belongs to its developers, whose names are too numerous
## to list here. Please refer to the COPYRIGHT file for details.
##
## SPDX-License-Identifier: GPL-2.0-or-later
##
## This file contains function for handling some keys in line edit mode.
## It is only used if the GAP kernel was compiled to use the GNU
## readline library.
##
## To avoid using the readline library, pass '--without-readline' to
## the configure script when compiling GAP.
##
# Declare the user preferences related to readline
# also if readline is not supported,
# in order to get them documented also in this case.
DeclareUserPreference( rec(
name:= [
"HistoryMaxLines",
"SaveAndRestoreHistory"],
description:= [
"<C>HistoryMaxLines</C> is the maximal amount of input lines held in \
&GAP;
's command line history.",
"If <C>SaveAndRestoreHistory</C> is <K>true</K> then &GAP; saves its \
command line history before terminating a &GAP; session,
and prepends the \
stored history when &GAP;
is started. \
If this
is enabled it
is suggested to set <C>HistoryMaxLines</C> to some \
finite value. \
It
is also possible to set <C>HistoryMaxLines</C> to <Ref Var=\
"infinity\"/> \
to keep arbitrarily many lines.
",
"These preferences are ignored if &GAP; was not compiled with \
readline support.
",
],
default:= [
10000,
true],
check:= function(max, save)
return ((IsInt( max )
and 0 <= max)
or max = infinity)
and save
in [
true,
false];
end
)
);
DeclareUserPreference( rec(
name :=
"Autocompleter",
description := [
"Set how names are filtered during tab-autocomplete, \
this can be: \
<C>\
"default\"</C>: case-sensitive matching. \
<C>\
"case-insensitive\"</C>: case-insensitive matching, \
or a record
with two components named <C>filter</C>
and \
<C>completer</C>, which are both functions which take two arguments. \
<C>filter</C> takes a list of names
and a partial identifier
and returns \
all the members of <C>names</C> which are a valid extension of the partial \
identifier. \
<C>completer</C> takes a list of names
and a partial identifier
and \
returns the partial identifier
as extended
as possible (it may also change \
the identifier,
for example to correct the case,
or spelling mistakes),
or \
returns <K>fail</K> to leave the existing partial identifier.
",
"This preference is ignored if &GAP; was not compiled with \
readline support.
",
],
default :=
"default",
) );
DeclareUserPreference( rec(
name:=
"HistoryBackwardSearchSkipIdenticalEntries",
description:= [
"When a command is executed multiple times, it is also stored in history \
multiple times. Setting this option to <K>
true</K> skips identical entries \
when searching backwards
in history.
"
],
default:=
false,
values:= [
true,
false ],
multi:=
false,
) );
if GAPInfo.CommandLineOptions.E then
############################################################################
## readline interface functions
GAPInfo.UseReadline :=
true;
## <#GAPDoc Label="readline">
## <Section Label="sec:readline">
## <Heading>Editing using the <C>readline</C> library</Heading>
##
## The descriptions in this section are valid only if your &GAP;
## installation uses the <C>readline</C> library for command line editing.
## You can check by <C>IsBound(GAPInfo.UseReadline);</C> if this is the
## case. <P/>
##
## You can use all the features of
## <C>readline</C>, as for example explained
## in <URL>https://tiswww.case.edu/php/chet/readline/rluserman.html</URL>.
## Therefore the command line editing in &GAP; is similar to the
## <C>bash</C> shell and many other programs. On a Unix/Linux system you
## may also have a manpage, try <C>man readline</C>. <P/>
##
## Compared to the command line editing which was used in &GAP; up to
## version 4.4 (or compared to not using the <C>readline</C> library)
## using <C>readline</C> has several advantages:
## <List>
## <Item>Most keys still do the same as explained in
## <Ref Sect="Line Editing"/> (in the default configuration).
## </Item>
## <Item>There are many additional commands, e.g. undoing (<B>Ctrl-_</B>,
## keyboard macros (<B>Ctrl-x(</B>, <B>Ctrl-x)</B> and <B>Ctrl-xe</B>),
## file name completion (hit <B>Esc</B> two or four times),
## showing matching parentheses,
## <C>vi</C>-style key bindings, deleting and yanking text, ...</Item>
## <Item>Lines which are longer than a physical terminal row can be edited
## more conveniently.</Item>
## <Item>Arbitrary unicode characters can be typed into string literals.
## </Item>
## <Item>The key bindings can be configured, either via your
## <File>~/.inputrc</File> file or by &GAP; commands, see <Ref
## Subsect="ssec:readlineCustom"/>.</Item>
## <Item>The command line history can be saved to and read from a file, see
## <Ref Subsect="ssec:cmdlinehistory"/>.</Item>
## <!-- <Item>demo mode <Ref Subsect="ssec:demoreadline"/>???</Item> -->
## <Item>Adventurous users can even implement completely new
## command line editing functions on &GAP; level, see <Ref
## Subsect="ssec:readlineUserFuncs"/>.</Item>
##
## </List>
## <P/>
##
## <Subsection Label="ssec:readlineCustom">
## <Index Key="ReadlineInitLine"><C>ReadlineInitLine</C></Index>
## <Heading>Readline customization</Heading>
##
## You can use your readline init file (by default <File>~/.inputrc</File>
## on Unix/Linux) to customize key bindings. If you want settings be used
## only within &GAP; you can write them between lines containing <C>$if
## GAP</C> and <C>$endif</C>. For a detailed documentation of the available
## settings and functions see <URL Text="here">
## https://tiswww.case.edu/php/chet/readline/rluserman.html</URL>.
##
## <Listing Type="From readline init file">
## $if GAP
## set blink-matching-paren on
## "\C-x\C-o": dump-functions
## "\ep": kill-region
## $endif
## </Listing>
##
## Alternatively, from within &GAP; the command
## <C>ReadlineInitLine(<A>line</A>);</C> can be used, where <A>line</A> is
## a string containing a line as in the init file.
## <P/>
##
## Caveat: &GAP; overwrites the following keys (after reading the
## <File>~/.inputrc</File> file): <C>\C-g</C>, <C>\C-i</C>, <C>\C-n</C>,
## <C>\C-o</C>, <C>\C-p</C>, <C>\C-r</C>, <C>\C-\</C>, <C>\e<</C>,
## <C>\e></C>, <C>Up</C>, <C>Down</C>, <C>TAB</C>, <C>Space</C>,
## <C>PageUp</C>, <C>PageDown</C>. So, do not redefine these in your
## <File>~/.inputrc</File>.
## <P/>
##
## Note that after pressing <B>Ctrl-v</B> the next special character is
## input verbatim. This is very useful to bind keys or key sequences.
## For example, binding the function key <B>F3</B> to the command
## <C>kill-whole-line</C> by using the sequence <B>Ctrl-v</B> <B>F3</B>
## looks on many terminals like this:
## <C>ReadlineInitLine("\"^[OR\":kill-whole-line");</C>.
## (You can get the line back later with <B>Ctrl-y</B>.)
## <P/>
##
## The <B>Ctrl-g</B> key can be used to type any unicode character by its code
## point. The number of the character can either be given as a count, or if the
## count is one the input characters before the cursor are taken (as decimal
## number or as hex number which starts with <C>0x</C>. For example, the
## double stroke character ℤ can be input by any of the three key
## sequences <B>Esc 8484 Ctrl-g</B>, <B>8484 Ctrl-g</B> or <B>0x2124
## Ctrl-g</B>.
## <P/>
##
## Some terminals bind the <B>Ctrl-s</B> and <B>Ctrl-q</B> keys to stop and
## restart terminal output. Furthermore, sometimes <B>Ctrl-\</B> quits a
## program. To disable this behaviour (and maybe use these keys for command
## line editing) you can use <C>Exec("stty stop undef; stty start undef;
## stty quit undef");</C> in your &GAP; session or your <F>gaprc</F> file
## (see <Ref Sect="sect:gap.ini"/>).
## <P/>
## </Subsection>
##
## <Subsection Label="ssec:cmdlinehistory">
## <Heading>The command line history</Heading>
##
## &GAP; can save your input lines for later reuse. The keys <B>Ctrl-p</B>
## (or <B>Up</B>), <B>Ctrl-n</B> (or <B>Down</B>),
## <B>ESC<</B> and <B>ESC></B> work as documented in <Ref
## Sect="Line Editing"/>, that is they scroll backward and
## forward in the history or go to its beginning or end.
## Also, <B>Ctrl-o</B> works as documented, it is useful for repeating a
## sequence of previous lines.
## (But <B>Ctrl-l</B> clears the screen as in other programs.)
## <P/>
##
## The command line history can be used across several instances of &GAP;
## via the following two commands.
## </Subsection>
##
## <ManSection >
## <Func Arg="[fname], [app]" Name="SaveCommandLineHistory" />
## <Returns><K>fail</K> or number of saved lines</Returns>
## <Func Arg="[fname], [app]" Name="ReadCommandLineHistory" />
## <Returns><K>fail</K> or number of added lines</Returns>
##
## <Description>
## The first command saves the lines in the command line history to the
## file given by the string <A>fname</A>. The default for <A>fname</A> is
## <F>history</F> in the user's &GAP; root path <C>GAPInfo.UserGapRoot</C>
## or <F>"~/.gap_hist"</F> if this directory does not exist.
## If the optional argument <A>app</A> is
## <K>true</K> then the lines are appended to that file otherwise the file
## is overwritten.
## <P/>
## The second command is the converse, it reads the lines from file
## <A>fname</A>. If the optional argument <A>app</A> is true the lines
## are appended to the history, else it <Emph>prepends</Emph> them.
## <P/>
## By default, the command line history stores up to 1000 input lines.
## command line history. This number may be restricted or enlarged via
## via <C>SetUserPreference("HistoryMaxLines", num);</C> which may be set
## to a non negative number <C>num</C> to store up to <C>num</C> input
## lines or to <K>infinity</K> to store arbitrarily many lines.
## An automatic storing and restoring of the command line history can
## be configured via
## <C>SetUserPreference("SaveAndRestoreHistory", true);</C>.
## <P/>
## Note that these functions are only available if your &GAP; is configured
## to use the <C>readline</C> library.
## </Description>
## </ManSection>
##
## <Subsection Label="ssec:readlineUserFuncs">
## <Index Key="InstallReadlineMacro"><C>InstallReadlineMacro</C></Index>
## <Index Key="InvocationReadlineMacro"><C>InvocationReadlineMacro</C></Index>
##
## <Heading>Writing your own command line editing functions</Heading>
## It is possible to write new command line editing functions in &GAP; as
## follows.
## <P/>
## The functions have one argument <A>l</A> which is a list with five
## entries of the form <C>[count, key, line, cursorpos, markpos]</C> where
## <C>count</C> and <C>key</C> are the last pressed key and its count
## (these are not so useful here because users probably do not want to
## overwrite the binding of a single key), then <C>line</C> is a string
## containing the line typed so far, <C>cursorpos</C> is the current
## position of the cursor (point), and <C>markpos</C> the current position
## of the mark.
## <P/>
## The result of such a function must be a list which can have various
## forms:
## <List >
## <Mark><C>[str]</C></Mark>
## <Item>with a string <C>str</C>. In this case the text <C>str</C> is
## inserted at the cursor position.</Item>
## <Mark><C>[kill, begin, end]</C></Mark>
## <Item> where <C>kill</C> is <K>true</K> or <K>false</K> and <C>begin</C>
## and <C>end</C> are positions on the input line. This removes the text
## from the lower position to before the higher position. If <C>kill</C>
## is <K>true</K> the text is killed, i.e. put in the kill ring for later
## yanking.
## </Item>
## <Mark><C>[begin, end, str]</C></Mark>
## <Item>where <C>begin</C> and <C>end</C> are positions on the input line
## and <C>str</C> is a string.
## Then the text from position <C>begin</C> to before <C>end</C> is
## substituted by <C>str</C>.
## </Item>
## <Mark><C>[1, lstr]</C></Mark>
## <Item>
## where <C>lstr</C> is a list of strings. Then these strings are displayed
## like a list of possible completions. The input line is not changed.
## </Item>
## <Mark><C>[2, chars]</C></Mark>
## <Item>where <C>chars</C> is a string. The characters from <C>chars</C>
## are used as the next characters from the input. (At most 512 characters
## are possible.)</Item>
## <Mark><C>[100]</C></Mark>
## <Item>This rings the bell as configured in the terminal.</Item>
## </List>
##
## In the first three cases the result list can contain a position as a
## further entry, this becomes the new cursor position. Or it
## can contain two positions as further entries, these become the new
## cursor position and the new position of the mark.
## <P/>
##
## Such a function can be installed as a macro for <C>readline</C> via
## <C>InstallReadlineMacro(name, fun);</C> where <C>name</C> is a string
## used as name of the macro and <C>fun</C> is a function as above.
## This macro can be called by a key sequence which is returned by
## <C>InvocationReadlineMacro(name);</C>.
## <P/>
## As an example we define a function which puts double quotes around the
## word under or before the cursor position. The space character, the
## characters in <C>"(,)"</C>, and the beginning and end of the line
## are considered as word boundaries. The function is then installed as a
## macro and bound to the key sequence <B>Esc</B> <B>Q</B>.
## <P/>
## <Log>
## gap> EditAddQuotes := function(l)
## > local str, pos, i, j, new;
## > str := l[3];
## > pos := l[4];
## > i := pos;
## > while i > 1 and (not str[i-1] in ",( ") do
## > i := i-1;
## > od;
## > j := pos;
## > while IsBound(str[j]) and not str[j] in ",) " do
## > j := j+1;
## > od;
## > new := "\"";
## > Append(new, str{[i..j-1]});
## > Append(new, "\"");
## > return [i, j, new];
## > end;;
## gap> InstallReadlineMacro("addquotes", EditAddQuotes);
## gap> invl := InvocationReadlineMacro("addquotes");;
## gap> ReadlineInitLine(Concatenation("\"\\eQ\":\"",invl,"\""));;
## </Log>
## </Subsection>
##
## </Section>
## <#/GAPDoc>
##
if not IsBound(GAPInfo.CommandLineEditFunctions) then
GAPInfo.CommandLineEditFunctions := rec(
# This is the GAP function called by the readline handler function
# handled-by-GAP (GAP_rl_func in src/sysfiles.c).
KeyHandler := function(l)
local macro, res, key;
# remember this key
key := l[
2];
res:=[];
if l[
2] >=
1000 then
macro := QuoInt(l[
2],
1000);
if IsBound(GAPInfo.CommandLineEditFunctions.Macros.(macro)) then
res := GAPInfo.CommandLineEditFunctions.Macros.(macro)(l);
fi;
else
if IsBound(GAPInfo.CommandLineEditFunctions.Functions.(l[
2])) then
res := GAPInfo.CommandLineEditFunctions.Functions.(l[
2])(l);
fi;
fi;
GAPInfo.CommandLineEditFunctions.LastKey := key;
return res;
end,
Macros := rec(),
Functions := rec(),
# here we save readline init lines for post restore
RLInitLines := [],
RLKeysGAPHandler := []
);
if IsHPCGAP then
GAPInfo.CommandLineEditFunctions :=
AtomicRecord(GAPInfo.CommandLineEditFunctions);
fi;
fi;
# wrapper around kernel functions to store data for post restore function
BindGlobal(
"ReadlineInitLine", function(str)
READLINEINITLINE(ShallowCopy(str));
Add(GAPInfo.CommandLineEditFunctions.RLInitLines, str);
end);
BindGlobal(
"BindKeysToGAPHandler", function(str)
BINDKEYSTOGAPHANDLER(ShallowCopy(str));
Add(GAPInfo.CommandLineEditFunctions.RLKeysGAPHandler, str);
end);
CallAndInstallPostRestore( function()
local clef, l, a;
clef := GAPInfo.CommandLineEditFunctions;
l := clef.RLKeysGAPHandler;
clef.RLKeysGAPHandler := [];
for a
in l do
BindKeysToGAPHandler(a);
od;
l := clef.RLInitLines;
clef.RLInitLines := [];
for a
in l do
ReadlineInitLine(a);
od;
end);
# bind macro to a key sequence
BindGlobal(
"BindKeySequence", function(seq, subs)
ReadlineInitLine(Concatenation(
"\"", seq, "\
": \"", subs, "\
""));
end);
# general utility functions
# ringing bell according to terminal configuration (rl_ding)
GAPInfo.CommandLineEditFunctions.Functions.RingBell := function(arg)
return [
100];
end;
# sends <Return> and so calls accept-line
GAPInfo.CommandLineEditFunctions.Functions.AcceptLine := function(arg)
return [
101];
end;
# cands is list of strings, this displays them as matches for completion
# (rl_display_match_list)
GAPInfo.CommandLineEditFunctions.Functions.DisplayMatches := function(cand)
return [
1, cand];
end;
# this inserts a sequence of keys given as string into the input stream
# (rl_stuff_char, up to 512 characters are accepted)
GAPInfo.CommandLineEditFunctions.Functions.StuffChars := function(str)
return [
2, str];
end;
GAPInfo.CommandLineEditFunctions.Functions.UnicodeChar := function(l)
local helper, j, i, hc, hex, c, pos, k;
# same as GAPDoc's UNICODE_RECODE.UTF8UnicodeChar
helper := function ( n )
local res, a, b, c, d;
res :=
"";
if n <
0 then
return fail;
elif n <
128 then
Add( res, CHAR_INT( n ) );
elif n <
2048 then
a := n mod
64;
b := (n - a) /
64;
Add( res, CHAR_INT( b +
192 ) );
Add( res, CHAR_INT( a +
128 ) );
elif n <
65536 then
a := n mod
64;
n := (n - a) /
64;
b := n mod
64;
c := (n - b) /
64;
Add( res, CHAR_INT( c +
224 ) );
Add( res, CHAR_INT( b +
128 ) );
Add( res, CHAR_INT( a +
128 ) );
elif n <
2097152 then
a := n mod
64;
n := (n - a) /
64;
b := n mod
64;
n := (n - b) /
64;
c := n mod
64;
d := (n - c) /
64;
Add( res, CHAR_INT( d +
240 ) );
Add( res, CHAR_INT( c +
128 ) );
Add( res, CHAR_INT( b +
128 ) );
Add( res, CHAR_INT( a +
128 ) );
else
return fail;
fi;
return res;
end;
# if count=1 we consider the previous characters
if l[
1] =
1 then
j := l[
4]-
1;
i := j;
hc :=
"0123456789abcdefABCDEF";
while i >
0 and l[
3][i]
in hc do
i := i-
1;
od;
if i>
1 and l[
3][i] =
'x' and l[
3][i-
1] =
'0' then
hex :=
true;
i := i-
1;
else
hex :=
false;
i := i+
1;
fi;
c :=
0;
if hex then
for k
in [i+
2..j] do
pos := Position(hc, l[
3][k]);
if pos >
16 then
pos := pos-
6;
fi;
c := c*
16+(pos-
1);
od;
else
for k
in [i..j] do
pos := Position(hc, l[
3][k]);
c := c*
10 + (pos-
1);
od;
fi;
return [i, j+
1, helper(c)];
else
return [helper(l[
1])];
fi;
end;
GAPInfo.CommandLineEditFunctions.Functions.
7 :=
GAPInfo.CommandLineEditFunctions.Functions.UnicodeChar;
BindKeysToGAPHandler(
"\007");
# The history is stored within the GAPInfo record. Several GAP level
# command line edit functions below deal with the history. The maximal
# number of lines in the history is configurable via a user preference.
# TODO: should it be made thread-local in HPC-GAP?
if not IsBound(GAPInfo.History) then
GAPInfo.History := rec(Lines := [], Pos :=
0, Last :=
0);
fi;
## We use key 0 (not bound) to add line to history.
GAPInfo.CommandLineEditFunctions.Functions.AddHistory := function(l)
local i, max, hist;
max := UserPreference(
"HistoryMaxLines");
# no history
if max <=
0 then
return [];
fi;
# no trailing white space
i :=
0;
while Length(l[
3]) >
0 and Last(l[
3])
in "\n\r\t " do
Remove(l[
3]);
i := i +
1;
od;
if Length(l[
3]) =
0 then
return [
false,
1, i+
1,
1];
fi;
hist := GAPInfo.History.Lines;
while Length(hist) >= max do
# overrun, throw oldest line away
Remove(hist,
1);
GAPInfo.History.Last := GAPInfo.History.Last -
1;
od;
Add(hist, l[
3]);
GAPInfo.History.Pos := Length(hist) +
1;
if i =
0 then
return [];
else
return [
false, Length(l[
3])+
1, Length(l[
3]) + i +
1];
fi;
end;
GAPInfo.CommandLineEditFunctions.Functions.
0 :=
GAPInfo.CommandLineEditFunctions.Functions.AddHistory;
## C-p: previous line starting like current before point
GAPInfo.CommandLineEditFunctions.Functions.BackwardHistory := function(l)
local hist, n, start;
if UserPreference(
"HistoryMaxLines") <=
0 then
return [];
fi;
hist := GAPInfo.History.Lines;
n := GAPInfo.History.Pos;
# searching backward in history for line starting with input before cursor
if l[
4] = Length(l[
3]) +
1 then
start := l[
3];
else
start := l[
3]{[
1..l[
4]-
1]};
fi;
while n >
1 do
n := n -
1;
if PositionSublist(hist[n], start) =
1 then
if UserPreference(
"HistoryBackwardSearchSkipIdenticalEntries")
and hist[n] = l[
3] then
continue;
fi;
GAPInfo.History.Pos := n;
GAPInfo.History.Last := n;
return [
1, Length(l[
3])+
1, hist[n], l[
4]];
fi;
od;
# not found, delete rest of line and wrap over
GAPInfo.History.Pos := Length(hist)+
1;
if Length(start) = Length(l[
3]) then
return [];
else
return [
false, l[
4], Length(l[
3])+
1];
fi;
end;
# bind to C-p and map Up-key
GAPInfo.CommandLineEditFunctions.Functions.(INT_CHAR(
'P') mod
32) :=
GAPInfo.CommandLineEditFunctions.Functions.BackwardHistory;
BindKeysToGAPHandler(
"\020");
ReadlineInitLine(
"\"\\eOA\
": \"\\C-p\
"");
ReadlineInitLine(
"\"\\e[A\
": \"\\C-p\
"");
## C-n: next line starting like current before point
GAPInfo.CommandLineEditFunctions.Functions.ForwardHistory := function(l)
local hist, n, start;
if UserPreference(
"HistoryMaxLines") <=
0 then
return [];
fi;
hist := GAPInfo.History.Lines;
n := GAPInfo.History.Pos;
if n > Length(hist) then
# special case on empty line, we don't wrap to the beginning, but
# the position of the last history use
if Length(l[
3]) =
0 and GAPInfo.History.Last < Length(hist) then
GAPInfo.History.Pos := GAPInfo.History.Last;
n := GAPInfo.History.Pos;
else
n :=
0;
fi;
fi;
# searching forward in history for line starting with input before cursor
if l[
4] = Length(l[
3]) +
1 then
start := l[
3];
else
start := l[
3]{[
1..l[
4]-
1]};
fi;
while n < Length(hist) do
n := n +
1;
if PositionSublist(hist[n], start) =
1 then
GAPInfo.History.Pos := n;
GAPInfo.History.Last := n;
return [
1, Length(l[
3])+
1, hist[n], l[
4]];
fi;
od;
# not found, delete rest of line and wrap over
GAPInfo.History.Pos := Length(hist)+
1;
if Length(start) = Length(l[
3]) then
return [];
else
return [
false, l[
4], Length(l[
3])+
1];
fi;
end;
# bind to C-n and map Down-key
GAPInfo.CommandLineEditFunctions.Functions.(INT_CHAR(
'N') mod
32) :=
GAPInfo.CommandLineEditFunctions.Functions.ForwardHistory;
BindKeysToGAPHandler(
"\016");
ReadlineInitLine(
"\"\\eOB\
": \"\\C-n\
"");
ReadlineInitLine(
"\"\\e[B\
": \"\\C-n\
"");
## ESC <: beginning of history
GAPInfo.CommandLineEditFunctions.Functions.BeginHistory := function(l)
if UserPreference(
"HistoryMaxLines") <=
0 or
Length(GAPInfo.History.Lines) =
0 then
return [];
fi;
GAPInfo.History.Pos :=
1;
GAPInfo.History.Last :=
1;
return [
1, Length(l[
3]), GAPInfo.History.Lines[
1],
1];
end;
GAPInfo.CommandLineEditFunctions.Functions.(INT_CHAR(
'<')) :=
GAPInfo.CommandLineEditFunctions.Functions.BeginHistory;
BindKeysToGAPHandler(
"\\e<");
## ESC >: end of history
GAPInfo.CommandLineEditFunctions.Functions.EndHistory := function(l)
if UserPreference(
"HistoryMaxLines") <=
0 or
Length(GAPInfo.History.Lines) =
0 then
return [];
fi;
GAPInfo.History.Pos := Length(GAPInfo.History.Lines);
GAPInfo.History.Last := GAPInfo.History.Pos;
return [
1, Length(l[
3]), GAPInfo.History.Lines[GAPInfo.History.Pos],
1];
end;
GAPInfo.CommandLineEditFunctions.Functions.(INT_CHAR(
'>')) :=
GAPInfo.CommandLineEditFunctions.Functions.EndHistory;
BindKeysToGAPHandler(
"\\e>");
## C-o: line after last choice from history (for executing consecutive
## lines from the history
GAPInfo.CommandLineEditFunctions.Functions.(INT_CHAR(
'O') mod
32) := function(l)
local n, cf;
cf := GAPInfo.CommandLineEditFunctions;
if IsBound(cf.ctrlo) then
n := GAPInfo.History.Last +
1;
if UserPreference(
"HistoryMaxLines") <=
0 or
Length(GAPInfo.History.Lines) < n then
return [];
fi;
GAPInfo.History.Last := n;
Unbind(cf.ctrlo);
return [
1, Length(l[
3]), GAPInfo.History.Lines[n],
1];
else
cf.ctrlo :=
true;
return GAPInfo.CommandLineEditFunctions.Functions.StuffChars(
"\015\017");
fi;
end;
BindKeysToGAPHandler(
"\017");
## C-r: previous line containing text between mark and point (including
## the smaller, excluding the larger)
GAPInfo.CommandLineEditFunctions.Functions.HistorySubstring := function(l)
local hist, n, txt, pos;
if UserPreference(
"HistoryMaxLines") <=
0 then
return [];
fi;
hist := GAPInfo.History.Lines;
n := GAPInfo.History.Pos;
# text to search
if l[
4] < l[
5] then
if l[
5] > Length(l[
3])+
1 then
l[
5] := Length(l[
3])+
1;
fi;
txt := l[
3]{[l[
4]..l[
5]-
1]};
else
if l[
5] <
1 then
l[
5] :=
1;
fi;
txt := l[
3]{[l[
5]..l[
4]-
1]};
fi;
while n >
1 do
n := n -
1;
pos := PositionSublist(hist[n], txt);
if pos <> fail then
GAPInfo.History.Pos := n;
return [
1, Length(l[
3])+
1, hist[n], pos + Length(txt), pos];
fi;
od;
# not found, do nothing and wrap over
GAPInfo.History.Pos := Length(hist)+
1;
return [];
end;
GAPInfo.CommandLineEditFunctions.Functions.(INT_CHAR(
'R') mod
32) :=
GAPInfo.CommandLineEditFunctions.Functions.HistorySubstring;
BindKeysToGAPHandler(
"\022");
############################################################################
##
#F SaveCommandLineHistory( [<fname>], [append] )
#F ReadCommandLineHistory( [<fname>] )
##
## Use the first command to write the currently saved command lines in the
## history to file <fname>. If not given the default file name 'history'
## in GAPInfo.UserGapRoot or '~/.gap_hist' is used.
## The second command prepends the lines from <fname> to the current
## command line history (as much as possible when the user preference
## HistoryMaxLines is less than infinity).
##
BindGlobal(
"SaveCommandLineHistory", function(arg)
local fnam, append, hist, out;
if Length(arg) >
0 then
fnam := arg[
1];
else
if IsExistingFile(GAPInfo.UserGapRoot) then
fnam := Concatenation(GAPInfo.UserGapRoot,
"/history");
else
fnam := UserHomeExpand(
"~/.gap_hist");
fi;
fi;
if true in arg then
append :=
true;
else
append :=
false;
fi;
hist := GAPInfo.History.Lines;
out := OutputTextFile(fnam, append);
if out = fail then
return fail;
fi;
SetPrintFormattingStatus(out,
false);
WriteAll(out,JoinStringsWithSeparator(hist,
"\n"));
WriteAll(out,
"\n");
CloseStream(out);
return Length(hist);
end);
BindGlobal(
"ReadCommandLineHistory", function(arg)
local hist, max, fnam, s, append;
hist := GAPInfo.History.Lines;
max := UserPreference(
"HistoryMaxLines");
if Length(arg) >
0 and IsString(arg[
1]) then
fnam := arg[
1];
else
if IsExistingFile(GAPInfo.UserGapRoot) then
fnam := Concatenation(GAPInfo.UserGapRoot,
"/history");
else
fnam := UserHomeExpand(
"~/.gap_hist");
fi;
fi;
if true in arg then
append :=
true;
else
append :=
false;
fi;
s := StringFile(fnam);
if s = fail then
return fail;
fi;
s := SplitString(s,
"",
"\n");
if append then
if Length(s) > max then
s := s{[
1..max]};
fi;
Append(hist, s);
if Length(hist) > max then
hist := hist{[Length(hist) - max..Length(hist)]};
fi;
else
if Length(hist) >= max then
return 0;
fi;
if Length(s) + Length(hist) > max then
s := s{[Length(s)-max+Length(hist)+
1..Length(s)]};
fi;
hist := Concatenation(s, hist);
fi;
GAPInfo.History.Lines := hist;
GAPInfo.History.Last :=
0;
GAPInfo.History.Pos := Length(hist) +
1;
return Length(s);
end);
### Free: C-g, C-^
## This deletes the content of current buffer line, when appending a space
## would result in a sequence of space- and tab-characters followed by the
## current prompt. Otherwise a space is inserted at point.
GAPInfo.DeletePrompts :=
true;
GAPInfo.CommandLineEditFunctions.Functions.SpaceDeletePrompt := function(l)
local txt, len, pr, i;
if GAPInfo.DeletePrompts <>
true or l[
4] =
1 or l[
3][l[
4]-
1] <>
'>' then
return [
" "];
fi;
txt := l[
3];
len := Length(txt);
pr := CPROMPT();
Remove(pr);
i :=
1;
while txt[i]
in "\t " do
i := i+
1;
od;
if len - i+
1 = Length(pr)
and txt{[i..len]} = pr then
return [
false,
1, i+Length(pr),
1];
fi;
return [
" "];
end;
GAPInfo.CommandLineEditFunctions.Functions.(INT_CHAR(
' ')) :=
GAPInfo.CommandLineEditFunctions.Functions.SpaceDeletePrompt;
BindKeysToGAPHandler(
" ");
# These methods implement 'extender' for standard case sensitive and
# case insensitive matching.
# These methods take a list of candidates (cand), and the current
# partially written identifier from the command line. They return a
# replacement for 'identifier'. This extends 'identifier'
# to include all characters which occur in all members of cand.
# In the case-insensitive case this matching is done case-insensitively,
# and we also change the existing letters of the identifier to match
# identifiers.
# When there is no extension or change, these methods return 'fail'.
BindGlobal(
"STANDARD_EXTENDERS", rec(
caseSensitive := function(cand, word)
local i, j, c, match;
i := Length(word);
while true do
if i = Length(cand[
1]) then
break;
fi;
c := cand[
1][i+
1];
match :=
true;
for j
in [
2..Length(cand)] do
if Length(cand[j]) <= i
or cand[j][i+
1] <> c then
match :=
false;
break;
fi;
od;
if not match then
break;
else
i := i+
1;
fi;
od;
if i > Length(word) then
return cand[
1]{[
1..i]};
else
return fail;
fi;
end,
caseInsensitive := function(cand, word)
local i, j, c, lowword, filtequal, match;
# Check if exactly 'word' exists, ignoring case.
lowword := LowercaseString(word);
# If there are several equal words, just pick the first one...
filtequal := First(cand, a -> LowercaseString(a) = lowword);
if filtequal <> fail then
return filtequal;
fi;
i := Length(word);
while true do
if i = Length(cand[
1]) then
break;
fi;
c := LowercaseChar(cand[
1][i+
1]);
match :=
true;
for j
in [
2..Length(cand)] do
if Length(cand[j]) <= i
or LowercaseChar(cand[j][i+
1]) <> c then
match :=
false;
break;
fi;
od;
if not match then
break;
else
i := i+
1;
fi;
od;
if i >= Length(word) then
return cand[
1]{[
1..i]};
else
return fail;
fi;
end,
));
# C-i: Completion as GAP level function
GAPInfo.CommandLineEditFunctions.Functions.Completion := function(l)
local cf, pos, word, wordplace, idbnd, i, cmps, r, searchlist, cand, j,
completeFilter, completeExtender, extension, hasbang;
completeFilter := function(filterlist, partial)
local pref, lowpartial;
pref := UserPreference(
"Autocompleter");
if pref =
"case-insensitive" then
lowpartial := LowercaseString(partial);
return Filtered(filterlist,
a -> PositionSublist(LowercaseString(a), lowpartial) =
1);
elif pref =
"default" then
return Filtered(filterlist, a-> PositionSublist(a, partial) =
1);
elif IsRecord(pref)
and IsFunction(pref.completer) then
return pref.completer(filterlist, partial);
else
ErrorNoReturn(
"Invalid setting of UserPreference 'Autocompleter'");
fi;
end;
completeExtender := function(filterlist, partial)
local pref;
pref := UserPreference(
"Autocompleter");
if pref =
"case-insensitive" then
return STANDARD_EXTENDERS.caseInsensitive(filterlist, partial);
elif pref =
"default" then
return STANDARD_EXTENDERS.caseSensitive(filterlist, partial);
elif IsRecord(pref)
and IsFunction(pref.extender) then
return pref.extender(filterlist, partial);
else
ErrorNoReturn(
"Invalid setting of UserPreference 'Autocompleter'");
fi;
end;
# check if Ctrl-i was hit repeatedly in a row
cf := GAPInfo.CommandLineEditFunctions;
if Length(l)=
6 and l[
6] =
true and cf.LastKey =
9 then
cf.tabcount := cf.tabcount +
1;
else
cf.tabcount :=
1;
Unbind(cf.tabrec);
Unbind(cf.tabbang);
Unbind(cf.tabcompnam);
fi;
pos := l[
4]-
1;
# in whitespace in beginning of line \t is just inserted
if ForAll([
1..pos], i -> l[
3][i]
in " \t") then
return [
"\t"];
fi;
# find word to complete
while pos >
0 and l[
3][pos]
in IdentifierLetters do
pos := pos-
1;
od;
wordplace := [pos+
1, l[
4]-
1];
word := l[
3]{[wordplace[
1]..wordplace[
2]]};
# see if we are in the case of a component name
while pos >
0 and l[
3][pos]
in " \n\t\r" do
pos := pos-
1;
od;
idbnd := IDENTS_BOUND_GVARS();
if pos >
0 and l[
3][pos] =
'.' then
cf.tabcompnam :=
true;
if cf.tabcount =
1 then
# try to find name of component object
i := pos;
while i >
0 and (l[
3][i]
in IdentifierLetters
or l[
3][i]
in ".!") do
i := i-
1;
od;
cmps := SplitString(l[
3]{[i+
1..pos]},
".");
hasbang := [];
i := Length(cmps);
while i >
0 do
# distinguish '.' from '!.' and record for each component which was used
if Last(cmps[i]) =
'!' then
hasbang[i] :=
true;
Remove(cmps[i]);
# remove the trailing '!'
else
hasbang[i] :=
false;
fi;
NormalizeWhitespace(cmps[i]);
if not IsValidIdentifier(cmps[i]) then
break;
fi;
i := i-
1;
od;
hasbang := hasbang{[i+
1..Length(cmps)]};
cmps := cmps{[i+
1..Length(cmps)]};
r := fail;
if Length(cmps) >
0 and cmps[
1]
in idbnd then
r := ValueGlobal(cmps[
1]);
for j
in [
2..Length(cmps)] do
if not hasbang[j-
1]
and IsRecord(r)
and IsBound(r.(cmps[j])) then
r := r.(cmps[j]);
elif hasbang[j-
1]
and (IsRecord(r)
or IsComponentObjectRep(r))
and IsBound(r!.(cmps[j])
) then
r := r!.(cmps[j]);
else
r := fail;
break;
fi;
od;
fi;
if IsRecord(r) or IsComponentObjectRep(r) then
cf.tabrec := r;
cf.tabbang := hasbang[Length(cmps)];
fi;
fi;
fi;
# now produce the searchlist
if IsBound(cf.tabrec) then
# the first two <TAB> hits try existing component names only first
if cf.tabbang then
searchlist := ShallowCopy(NamesOfComponents(cf.tabrec));
elif IsRecord(cf.tabrec) then
searchlist := ShallowCopy(RecNames(cf.tabrec));
else
searchlist := [];
fi;
if cf.tabcount > 2 then
Append(searchlist, ALL_RNAMES());
fi;
else
# complete variable name
searchlist := idbnd;
fi;
cand := completeFilter(searchlist, word);
# in component name search we try again with all names if this is empty
if IsBound(cf.tabcompnam) and Length(cand) = 0 and cf.tabcount < 3 then
searchlist := ALL_RNAMES();
cand := completeFilter(searchlist, word);
fi;
if (not IsBound(cf.tabcompnam) and cf.tabcount = 2) or
(IsBound(cf.tabcompnam) and cf.tabcount in [2,4]) then
if Length(cand) > 0 then
# we prepend the partial word which was completed
return GAPInfo.CommandLineEditFunctions.Functions.DisplayMatches(
Concatenation([word], Set(cand)));
else
# ring the bell
return GAPInfo.CommandLineEditFunctions.Functions.RingBell();
fi;
fi;
if Length(cand) = 0 then
return [];
elif Length(cand) = 1 then
return [ wordplace[1], wordplace[2]+1, cand[1]{[1..Length(cand[1])]}];
fi;
extension := completeExtender(cand, word);
if extension = fail then
return [];
else
return [ wordplace[1], wordplace[2] + 1, extension ];
fi;
end;
GAPInfo.CommandLineEditFunctions.Functions.(INT_CHAR('I') mod 32) :=
GAPInfo.CommandLineEditFunctions.Functions.Completion;
BindKeysToGAPHandler("\011");
#############################################################################
##
## Simple utilities to create an arbitrary number of macros
##
# name a string, fun a function
InstallReadlineMacro := function(name, fun)
local cfm, pos;
cfm := GAPInfo.CommandLineEditFunctions.Macros;
if not IsBound(cfm.Names) then
cfm.Names := [];
fi;
pos := Position(cfm.Names, name);
if pos = fail then
pos := Length(cfm.Names)+1;
fi;
cfm.(pos) := fun;
cfm.Names[pos] := name;
end;
# A sequence to invoce macro name ('ESC num C-x C-g' sets GAPMacroNumber in
# kernel and then any key that calls handled-by-GAP will do it)
# We assume that 'C-xC-g' and <TAB> are not overwritten.
InvocationReadlineMacro := function(name)
local cfm, pos;
cfm := GAPInfo.CommandLineEditFunctions.Macros;
if not IsBound(cfm.Names) then
cfm.Names := [];
fi;
pos := Position(cfm.Names, name);
if pos = fail then
return fail;
fi;
return Concatenation("\033", String(pos), "\030\007\t");
end;
## # Example
## gap> InstallReadlineMacro("My Macro", function(l) return ["my text"]; end);
## gap> InvocationReadlineMacro("My Macro");
## "\0331\030\007\t"
## gap> BindKeySequence("^[OR",last); # first arg with C-v<F3>
# for compatibility with the non-readline kernel code
LineEditKeyHandlers := [];
LineEditKeyHandler := function(l)
return [l[1], l[3], l[5]];
end;
else
# some experimental code, use readline instead
ReadLib("cmdleditx.g");
fi;