New python mode for JED editor
Brien Barton
brien_barton at hotmail.com
Fri Sep 10 12:49:57 EDT 1999
%There is a bug in the auto-indent logic of the Python mode for JED
%(releases up to and including B0.99.8). The following is an updated
%version of pymode.sl that fixes the problem. This code has already
%been submitted to John Davis for inclusion in the next JED release.
%
%----------- cut here --------------
% Python mode
% File: pymode.sl v1.3
%
% For editing source code written in the Python programming language.
% Provides basic compatibility with Python mode under real Emacs
%
% Authors: Harri Pasanen <hpa at iki.fi>
% Brien Barton <brien_barton at hotmail.com>
%
% following keys have python specific bindings:
%
% DELETE deletes to previous indent level
% TAB indents line
% ^C# comments region or current line
% ^C> shifts line or region right
% ^C< shifts line or region left
% ^C^C executes the region, or the buffer if region not marked.
% ^C| executes the region
% ^C\t reindents the region
% : colon dedents appropriately
%
% See python_mode function for available hooks
%
% Shortcomings: does not really support triple-quoted strings in any way.
%
% Changes from v1.0:
%
% Major improvements, mostly done by Brien Barton:
%
% - execution of python code from JED
% - DFA syntax support
% - improved indent - dedent.
%
% Changes from v1.1:
%
% Minor fixes, by Tom Culliton
%
% - corrected a syntax error
% - fixed non-DFA syntax hilighting tables to work better
% - added the new assert keyword
% Changes from v1.2
% - autoindent correction
$1 = "python";
!if (keymap_p ($1)) make_keymap ($1);
definekey ("py_backspace_key", "^?", $1);
!if (is_defined ("Win_Keys")) { % Ctrl-C conflicts with windows region
copy.
definekey ("py_comment_region", "^C#", $1);
definekey ("py_uncomment_region", "^C3", $1);
definekey ("py_shift_region_right", "^C>", $1);
definekey ("py_shift_region_left", "^C<", $1);
definekey ("py_exec", "^C^C", $1); % Execute buffer, or region if
defined
definekey ("py_exec_region", "^C|", $1);
definekey ("py_reindent_region", "^C\t", $1);
}
definekey ("py_electric_colon", ":", $1);
#ifdef MSWINDOWS
definekey ("py_help_on_word", "^@;", $1);
#endif
% Set the following to your favourite indentation level
!if (is_defined ("Py_Indent_Level")) { % users can set this in .jedrc
variable Py_Indent_Level = 4;
}
define py_line_ends_with_colon()
{
eol();
if (bfind_char(':')) {
go_right(1);
skip_white();
if (eolp() or looking_at_char('#'))
return 1;
}
return 0;
}
define py_endblock_cmd()
{
bol_skip_white();
if (looking_at("return") or
looking_at("raise") or
looking_at("break") or
looking_at("pass") or
looking_at("continue"))
return 1;
return 0;
}
define py_line_starts_subblock()
{
bol_skip_white();
if (looking_at("else") or
looking_at("elif") or
looking_at("except") or
looking_at("finally"))
return 1;
return 0;
}
define py_line_starts_block()
{
bol_skip_white();
if (looking_at("if") or
looking_at("try") or
py_line_starts_subblock())
return 1;
return 0;
}
define py_indent_calculate()
{ % return the indentation of the previous python line
variable col = 0;
variable subblock = 0;
EXIT_BLOCK
{
pop_spot ();
return col;
}
% check if current line starts a sub-block
subblock = py_line_starts_subblock();
% go to previous non blank line
push_spot_bol ();
!if (re_bsearch ("[^ \t\n]"))
return;
bol_skip_white();
col = what_column() - 1;
if (py_line_ends_with_colon())
col += Py_Indent_Level;
if (py_endblock_cmd() or (subblock and not py_line_starts_block()))
col -= Py_Indent_Level;
}
define py_indent_line()
{
variable col;
col = py_indent_calculate();
bol_trim ();
whitespace( col );
}
define py_comment_line()
{
bol();
insert("##");
}
define py_electric_colon()
{
variable i;
insert(":");
push_spot();
if (py_line_starts_subblock()) % Only dedents on colon
{
pop_spot();
i = what_column();
bol_skip_white();
i = i - what_column();
if (py_indent_calculate() < what_column()) % Ensure dedent only
py_indent_line();
bol_skip_white();
goto_column( i + what_column() );
}
else
pop_spot();
}
define py_comment_region()
{
variable n;
check_region (1);
n = what_line ();
pop_mark_1 ();
loop (n - what_line ())
{
py_comment_line();
go_down_1 ();
}
pop_spot();
}
define py_comment()
{
push_spot();
if (markp()) {
py_comment_region();
} else {
py_comment_line();
}
pop_spot();
}
define py_uncomment_line()
{
bol_skip_white();
while (looking_at("#")) del();
}
define py_uncomment_region()
{
variable n;
check_region (1);
n = what_line ();
pop_mark_1 ();
loop (n - what_line ())
{
py_uncomment_line();
go_down_1 ();
}
pop_spot();
}
define py_uncomment() {
push_spot();
if (markp()) {
py_uncomment_region();
} else {
py_uncomment_line();
}
pop_spot();
}
define py_backspace_key()
{
variable col;
col = what_column();
push_spot();
bskip_white();
if (bolp() and (col > 1)) {
pop_spot();
bol_trim ();
col--;
if (col mod Py_Indent_Level == 0)
col--;
whitespace ( (col / Py_Indent_Level) * Py_Indent_Level );
}
else {
pop_spot();
call("backward_delete_char_untabify");
}
}
define py_shift_line_right()
{
bol_skip_white();
whitespace(Py_Indent_Level);
}
define py_shift_region_right()
{
variable n;
check_region (1); % spot_pushed, now at end o
f region
n = what_line ();
pop_mark_1 ();
loop (n - what_line ())
{
py_shift_line_right();
go_down_1 ();
}
pop_spot();
}
define py_shift_right()
{
push_spot();
if (markp()) {
py_shift_region_right();
} else {
py_shift_line_right();
}
pop_spot();
}
define py_shift_line_left()
{
bol_skip_white();
if (what_column() > Py_Indent_Level) {
push_mark();
goto_column(what_column() - Py_Indent_Level);
del_region();
}
}
define py_shift_region_left()
{
variable n;
check_region (1);
n = what_line ();
pop_mark_1 ();
loop (n - what_line ())
{
py_shift_line_left();
go_down_1 ();
}
pop_spot();
}
define py_shift_left() {
push_spot();
if (markp()) {
py_shift_region_left();
} else {
py_shift_line_left();
}
pop_spot();
}
define py_newline_and_indent()
{
newline();
py_indent_line();
}
define file_path(fullname)
{
variable filename;
filename = extract_filename(fullname);
substr(fullname, 1, strlen(fullname)-strlen(filename));
}
define py_exec_region()
{
% Run python interpreter on current region.
% Display output in *shell-output* buffer window.
variable oldbuf, thisbuf, file, line, start_line;
variable tmpfile = "_python.tmp";
variable error_regexp = "^ File \"\\([^\"]+\\)\", line \\(\\d+\\).*";
variable py_source = buffer_filename();
change_default_dir(file_path(py_source));
thisbuf = whatbuf();
% Check if 1st line starts in column 1
exchange_point_and_mark();
bol_skip_white();
start_line = what_line();
if (what_column() > 1) {
% Workaround in case block is indented
write_string_to_file("if 1:\n", tmpfile); bol();
start_line--; % offset for this extra line
}
exchange_point_and_mark();
append_region_to_file(tmpfile);
oldbuf = pop2buf_whatbuf("*shell-output*"); erase_buffer ();
#ifdef UNIX
r
un_shell_cmd(sprintf("python %s 2>&1", tmpfile));
#else
run_shell_cmd(sprintf("python %s", tmpfile));
#endif
() = delete_file(tmpfile);
% try to restore any window that got replaced by the shell-output
if (strlen(oldbuf) and (strcmp(oldbuf, "*shell-output*") != 0)
and (strcmp(thisbuf, oldbuf) != 0))
splitwindow(); sw2buf(oldbuf); pop2buf("*shell-output*");
}
eob();
% Check for error message
while (re_bsearch(error_regexp) != 0)
% Make sure error occurred in the file we were executing
file = regexp_nth_match(1);
line = integer(regexp_nth_match(2));
if (strcmp(file, tmpfile) == 0)
% Move to line in source that generated the error
pop2buf(thisbuf);
goto_line(line + start_line - 1);
break;
} else
% Error is in another file, try previous error message
continue;
}
}
}
define py_exec()
{
% Run python interpreter on current region if one is defined, otherwise
% on the whole buffer.
% Display output in *shell-output* buffer window.
!if (markp()) { % create region containing entire buffer
push_spot_bob ();
push_mark_eob ();
}
py_exec_region();
}
define py_reindent() {
% Reindents a (correctly) indented buffer using the current
% value of Py_Indent_Level.
% Warning: Current version can be fooled by implicit or explicit
% continuation lines.
variable indent_level = create_array('i', 64, 1);
variable level = -1;
variable current_indent = -1;
variable errmsg, i, col, ignore, oldlevel;
for (i = 0; i < 64; i++) {
indent_level[i] = -1;
}
bob();
do {
bol_skip_white();
ignore = looking_at_char('#') or eolp();
if (ignore) continue; % skip comments and blank lines
col = what_column() - 1;
oldlevel = level; % save current level
if (col > current_indent) { % indenting
level++;
} else if (col < current_indent) { % dedent
while ((level > 0) and (indent_level[level] > col)) {
indent_level[level] = -1; % clear current level setting
level--;
}
}
if ((indent_level[level] != -1) and (indent_level[level] != col)) {
% Indent is wrong. Hopefully it's a continuation line.
level = oldlevel; % reset level
bol_trim();
whitespace(level * Py_Indent_Level + (col - current_indent));
} else {
current_indent = col;
indent_level[level] = col;
bol_trim();
whitespace(level * Py_Indent_Level);
}
} while (down(1) == 1);
}
define py_reindent_region()
{
narrow();
py_reindent();
widen();
}
#ifdef MSWINDOWS
define py_help_on_word()
{
variable tag = "0-9A-Z_a-z";
push_spot ();
skip_white ();
bskip_chars (tag);
push_mark ();
skip_chars (tag);
tag = bufsubstr (); % leave on the stack
pop_spot ();
message( strcat("Help on ", tag) );
msw_help( getenv("PYLIBREF"), tag, 0);
}
#endif
create_syntax_table ($1);
define_syntax ("#", "", '%', $1); % comments
define_syntax ("([{", ")]}", '(', $1); % delimiters
define_syntax ('"', '"', $1); % quoted strings
define_syntax ('\'', '\'', $1); % quoted strings
define_syntax ('\\', '\\', $1); % continuations
define_syntax ("0-9a-zA-Z_", 'w', $1); % words
define_syntax ("-+0-9a-fA-FjJlLxX.", '0', $1); % Numbers
define_syntax (",;.:", ',', $1); % punctuation
define_syntax ("%-+/&*=<>|!~^`", '+', $1); % operators
set_syntax_flags ($1, 1); % keywords ARE case-sensitive
() = define_keywords ($1, "ifinisor", 2); % all keywords of length 2
() = define_keywords ($1, "anddefdelfornottry", 3); % of length 3 ....
() = define_keywords ($1, "elifelseexecfrompass", 4);
() = define_keywords ($1, "breakclassprintraisewhile", 5);
() = define_keywords ($1, "assertexceptglobalimportlambdareturn", 6);
() = define_keywords ($1, "finally", 7);
() = define_keywords ($1, "continue", 8);
% Type 1 keywords (actually these are what's in __builtins__)
() = define_keywords_n ($1, "id", 2, 1);
() = define_keywords_n ($1, "abschrcmpdirhexintlenmapmaxminoctordpowstr", 3,
1);
() = define_keywords_n ($1, "Noneevalhashlongopenreprtypevars", 4, 1);
() = define_keywords_n ($1, "applyfloatinputrangeroundtuple", 5, 1);
() = define_keywords_n ($1, "coercedivmodfilterlocalsreducereloadxrange", 6,
1);
() = define_keywords_n ($1,
"IOError__doc__compiledelattrgetattrglobalshasattrsetattr", 7, 1);
() = define_keywords_n ($1, "EOFErrorKeyError__name__callableexecfile", 8,
1);
() = define_keywords_n ($1, "NameErrorTypeErrorraw_input", 9, 1);
() = define_keywords_n ($1, "IndexErrorSystemExitValueError__import__", 10,
1);
() = define_keywords_n ($1,
"AccessErrorImportErrorMemoryErrorSyntaxErrorSystemError", 11, 1);
() = define_keywords_n ($1, "RuntimeError", 12, 1);
() = define_keywords_n ($1, "ConflictErrorOverflowError", 13, 1);
() = define_keywords_n ($1, "AttributeError", 14, 1);
() = define_keywords_n ($1, "KeyboardInterruptZeroDivisionError", 17, 1);
#ifdef HAS_DFA_SYNTAX
enable_highlight_cache("python.dfa", $1);
define_highlight_rule("\"\"\".+\"\"\"", "string", $1); % long string (""")
define_highlight_rule("'''.+'''", "string", $1); % long string (''')
define_highlight_rule("\"[^\"]*\"", "string", $1); % normal string
define_highlight_rule("'[^']*'", "string", $1); % normal string
define_highlight_rule("#.*", "comment", $1); % comment
define_highlight_rule("[A-Za-z_][A-Za-z_0-9]*", "Knormal", $1); % identifier
define_highlight_rule("[1-9][0-9]*[lL]?", "number", $1); % decimal int
define_highlight_rule("0[0-7]*[lL]?", "number", $1); % octal int
define_highlight_rule("0[xX][0-9a-fA-F]+[lL]?", "number", $1); % hex int
define_highlight_rule("[1-9][0-9]*\\.[0-9]*([Ee][\\+\\-]?[0-9]+)?",
"number", $1); % float n.[n]
define_highlight_rule("0?\\.[0-9]+([Ee][\\+\\-]?[0-9]+)?",
"number", $1); % float [n].n
define_highlight_rule("[ \t]+", "normal", $1);
define_highlight_rule("[\\(\\[{}\\]\\),:\\.\"`'=;]", "delimiter", $1);
define_highlight_rule("[\\+\\-\\*/%<>&\\|\\^~]", "operator", $1); % 1 char
define_highlight_rule("<<|>>|==|<=|>=|<>|!=", "operator", $1); % 2 char
% Flag badly formed numeric literals or identifiers. This is more effective
% if you change the error colors so they stand out.
define_highlight_rule("[1-9][0-9]*[lL]?[0-9A-Za-z\\.]+", "error", $1); % bad
decimal
define_highlight_rule("0[0-7]+[lL]?[0-9A-Za-z\\.]+", "error", $1); % bad
octal
define_highlight_rule("0[xX][0-9a-fA-F]+[lL]?[0-9A-Za-z\\.]+", "error", $1);
% bad hex
define_highlight_rule("\\.[0-9]+([Ee][\\+\\-]?[0-9]+)?[A-Za-z]+", "error",
$1); % bad float
define_highlight_rule("[A-Za-z_][A-Za-z_0-9]*\\.[0-9]+[A-Za-z]*", "error",
$1); % bad identifier
build_highlight_table($1);
#endif
%!%+
%\function{python_mode}
%\synopsis{python_mode}
%\usage{python_mode ()}
%\description
% A major mode for editing python files.
%
% The following keys have python specific bindings:
%#v+
% DELETE deletes to previous indent level
% TAB indents line
% ^C# comments region or current line
% ^C> shifts line or region right
% ^C< shifts line or region left
% ^C^C executes the region, or the buffer if region not marked.
% ^C| executes the region
% ^C\t reindents the region
% : colon dedents appropriately
%#v-
% Hooks: \var{python_mode_hook}
%
%\seealso{Py_Indent_Level}
%\seealso{set_mode, c_mode}
%!%-
define python_mode ()
{
variable python = "python";
TAB = 8;
set_mode (python, 0x4); % flag value of 4 is generic language mode
use_keymap(python);
set_buffer_hook ("indent_hook", "py_indent_line");
set_buffer_hook ("newline_indent_hook", "py_newline_and_indent");
use_syntax_table (python);
runhooks("python_mode_hook");
}
More information about the Python-list
mailing list