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