jed-users mailing list

[2003 Date Index] [2003 Thread Index] [Other years]
[Thread Prev] [Thread Next]      [Date Prev] [Date Next]

view mode for readonly buffers


Dear jedies,

Together with Paul Boekholt, I wrote a very small view mode that
  - can be used as default for buffers in readonly mode
    (can also be used as dropin replacement for the most_mode
  - serves as a base for other modes as (hyper-) help, (hyper-) man,
    filelist (directory-mode), grep, ch-table, ...
    by offering a generic readonly-keymap from which the other modes derive
    their keymaps via copy_keymap.
    
    
Why not use the most_mode?
  - view_mode resembles the more commonly used pager less + the
    general jed-features based on your emulation (emacs, ide, cua).
  - is a little bit smaller
  - works well with (and relies on) the bufutils.sl mode, especially
  - offers support for the popup-buffer feature (used in hyperhelp and other
    of my modes) via the binding to the close_buffer() function.
        (As long as there is no "_jed_close_buffer_hook", a special 
	close_buffer function is needed to do the cleanup when a buffer is
	closed.)

The mode is still under developement, so your suggestions are especially
welcome. Especially the keymap-layout relays on your feedback: as the buffer
is readonly, a lot of keybindings are free: shall we bind "as much as
possible" just sparing "dangerous" keybindings or keep to a minimalistic set
that is common for all. Also an emulation-dependent part is feasible.

Günter

-- 
Milde at ife.et.tu-dresden.de
% a generic view mode for readonly buffers
%
% If you want all buffers opened readonly to have this mode, you can do
%   autoload("view_mode_if_readonly", "view");
%   append_to_hook("_jed_find_file_after_hooks", &view_mode_if_readonly);
% in your .jedrc

static variable mode = "view";

% requirements

autoload("close_buffer", "bufutils");
autoload("set_help_message", "bufutils");
autoload("help_message", "bufutils");

% customization
% Ask before going to edid mode?
custom_variable("View_Edit_Ask", 1);

% --- helper functions ---

% Make a readonly buffer editable (this is from most.sl)
define enable_edit()
{
   if(andelse
      {View_Edit_Ask}
      {get_y_or_n("Edit this buffer") == 1}
     )
     {
	set_readonly(0);
	set_status_line("", 0);  % reset to global
	runhooks("mode_hook", file_type(buffer_filename));
     }
}

% this one is also in cuamisc.sl (and hopefully one day in site.sl)!
%!%+
%\function{repeat_search}
%\synopsis{continue searching with last searchstring}
%\usage{define repeat_search ()}
%\seealso{LAST_SEARCH, search_forward, search_backward}
%!%-
define repeat_search ()
{
   go_right (1);
   !if (fsearch(LAST_SEARCH)) error ("Not found.");
}

% --- the mode ---

% a generic keymap for readonly buffers (use for view-mode or
% as base for your specific map with copy_keymap(mode, "view"))
!if (keymap_p(mode))
{
   make_keymap(mode);
%    _for ('a', 'z', 1)
%      definekey(char, "error(Help_Message[normalized_modename])", _stk_roll(2), mode);
   definekey("close_buffer",                     "\e\e\e", mode); % Escape
   definekey("page_up", 		         Key_BS,   mode);
   definekey("page_down",                        " ",      mode);
   % TODO: Key_Return/Key_BS  scroll one line down/up
   definekey("bob",                              "<",      mode);
   definekey("eob; recenter(window_info('r'));", ">",      mode);
   definekey("re_search_forward",                "/",      mode);
   definekey("repeat_search",                    "\\",     mode);
   definekey("help_message",                     "?",      mode);
   definekey("search_backward",                  "b",	   mode);
   definekey("enable_edit",                      "e",      mode);
   definekey("search_forward",                   "f",	   mode);
   definekey("goto_line",                        "g",      mode);
   definekey("describe_mode",                    "h",      mode);
%    definekey("page_down",                        "n",      mode);
%    definekey("page_up",                          "p",      mode);
   definekey("close_buffer",                     "q",      mode);
   definekey("isearch_forward",                  "s",      mode);
   _for (0, 9, 1)
     definekey("digit_arg", string(_stk_roll(2)), mode);
}

public define view_mode()
{
   set_readonly(1);
   set_buffer_modified_flag(0);
   use_keymap(mode);
   set_mode(mode, 0);
   set_help_message(
     "SPC:pg_dn BS:pg_up f:search_fw b:search_bw q:quit e:edit ?:this_help");
   run_mode_hooks(mode);
}

public define view_mode_if_readonly()
{
   if (is_readonly)
     view_mode;
}

provide(mode);
% bufutils.sl  Tools for buffer and windows handling
% by Guenter Milde <g.milde@xxxxxx>
% Version 1.0   first public version
%         1.1   bugfix: restore_buffer now resets the "changed on disk" flag
%         1.2   new: "blocal_hooks"
%         1.2.2 "outsourcing" of window_set_rows (hint by Thomas Clausen)
%	  1.3   moved most often used programming helpers to sl_utils.sl
%	        new: [key_prefix=""] argument to rebind, rebind_reserved
%	             (hint and bugfix Paul Boekholt)
%	        rework of popup_buffer 
%	          - do not reuse popups
%	          - reload old buffer when closing (Paul Boekholt)
%	  1.4   new: readonly-map, view_mode
%	  	     help_message(): Give mode-dependend help message
%	  	     
%	  	removed next_buffer() (now in cuamisc.sl)

require("keydefs");
autoload("get_blocal", "sl_utils");
autoload("push_defaults", "sl_utils");
autoload("run_function", "sl_utils");
autoload("get_word", "txtutils");
autoload("mark_word", "txtutils");

%!%+
%\function{buffer_dirname}
%\synopsis{Return the directory associated with the buffer}
%\usage{Str buffer_dirname()}
%\description
%   Return the directory associated with the buffer}
%\seealso{getbuf_info, buffer_filename}
%!%-
define buffer_dirname()
{
   variable dir, args = __pop_args(_NARGS);
   ( , dir, , ) = getbuf_info(__push_args(args));
   return dir;
}

% % not really a buffer operation
% define autoload_if_present(fun, file)
% {
%    if(strlen(expand_jedlib_file(file)))
%      autoload(fun, file);
% }

% --- window operations ----------------------------------------------

%!%+
%\function{window_set_rows}
%\synopsis{Make the current window \var{rows} rows big}
%\usage{Void window_set_rows(Int rows)}
%\description
%   Resizes the current window:
%   If there is only one window, the no action is taken.
%   If \var{rows} is zero, the window is deleted
%   If \var{rows} is negative, the window is reduced by \var{rows} lines.
%   (Use loop(rows) enlargewin(); to get relative enlargement.)
%\notes
%   If there are more than two windows open, 
%   the function might not work as desired.
%\seealso{fit_window, enlargewin, onewindow}
%!%-
public define window_set_rows(rows)
{
   if (rows == 0)
     	call("delete_window");
   if (rows < 0)
       rows += window_info('r');
   if (nwindows () - MINIBUFFER_ACTIVE == 1)
     return;
   if (rows >= SCREEN_HEIGHT-3)
     onewindow();
   variable misfit = rows - window_info('r');
   if (misfit > 0) { % window too small
      loop(misfit)
	enlargewin ();
   }
   if (misfit < 0) { % window too large
      variable curbuf = whatbuf();
      otherwindow();
      loop(-misfit)
	enlargewin ();
      loop(nwindows() - 1)
	otherwindow();
   }
   recenter(what_line);
}

%!%+
%\function{fit_window}
%\synopsis{fits the window size to the lenght of the buffer}
%\usage{ Void fit_window (max_rows=1.0)}
%\description
% the optional parameter max_rows gives the maximal size of the window,
% either as proportion of the total space or as fix number of lines.
% The default max_rows=1.0 means no limit, max_rows=0 means: don't fit.
%\seealso{enlargewin, popup_buffer}
%!%-
public define fit_window () % fit_window(max_rows = 1.0)
{
   variable max_rows = 1.0;
   if (_NARGS)
     max_rows = ();
   
   if (max_rows == 0)
     return;
   % convert max_rows from fraction to absolute if Double_Type:
   if (typeof(max_rows) == Double_Type)
	max_rows = int(SCREEN_HEIGHT-3 * max_rows);
   % get the desired number of rows (lines in the actual buffer or max_rows)
   push_spot();
   eob;
   variable wanted_rows = what_line;
   pop_spot();
   % limit to max_rows
   if (wanted_rows > max_rows)
     wanted_rows = max_rows;
   % fit window
   window_set_rows(wanted_rows);
}

% --- closing the buffer -------------------------------------------------

%!%+
%\function{close_buffer}
%\synopsis{Close the current (or given) buffer}
%\usage{ Void close_buffer(buf = whatbuf())}
%\description
%   Close the current (or given) buffer.
%   If it is a popup_buffer, close also the containing window.
%\seealso{delbuf, close_window, popup_buffer}
%!%-
public define close_buffer() % (buf = whatbuf())
{
   % get optional argument(s)
   variable buf, oldbuf;
   if (_NARGS)
     buf = ();
   else
     buf = whatbuf();
   if (buf == whatbuf() and buffer_visible(buf))
     {
        % close popup windows, if the buffer is visible and active
        if (get_blocal("is_popup", 0) != 0)
          call("delete_window");
        else if (blocal_var_exists("is_popup")) % popup in permanent window
          {
             oldbuf = get_blocal_var("oldbuf");
             if (bufferp(oldbuf))
               sw2buf(oldbuf);
             otherwindow();
          }
     }
   delbuf(buf);
   % resize popup windows
   fit_window(get_blocal("is_popup", 0));
}

% close buffer in second window if there are two windows
define close_other_buffer ()
{
   if (nwindows () - MINIBUFFER_ACTIVE < 2)
     return;
   otherwindow();
   close_buffer();
}

% Close buffer, insert current word in calling buffer
% Will not insert, if txtutils not present...
define close_and_insert_word()
{
   variable word = get_word;
   close_buffer();
   insert(word);
   sw2buf(whatbuf); % paranoia: show current buffer
}

% Close buffer, replace current word in calling buffer with current word
% Will not insert, if txtutils not present...
define close_and_replace_word()
{
   variable word = get_word;
   close_buffer();
   !if (is_visible_mark)
     mark_word;
   del_region();
   insert(word);
   sw2buf(whatbuf); % paranoia: show current buffer
}

% open buffer, preserve the number of windows currently open
define go2buf(buf)
{
   if(buffer_visible(buf))
     pop2buf(buf);   % open in other window
   else
     sw2buf(buf);    % open in current window
}

% --- "Popup Buffer" -----------------------------------------------------

custom_variable("Max_Popup_Size", 0.7);          % max size of one popup window
% Todo: more than 1 popup window in parrallel (i.e. more than 2 open windows)
% custom_variable("Popup_max_popups", 2);        % max number of popup windows
% custom_variable("Popup_max_total_size", 0.7);  % max size of all popup windows

% a "popup buffer": 
%   - open in a new window, if there is enough space
%   - fit window, if there is only one window before 
%     or the new buffer replaces a popup_window
%     and if the buffer is not empty 
%     (To open a popup buffer, fill it an fit if applicable, either do
%        sw2buf("popbuf"), <insert stuff>, popup_buffer();
%      or
%        popup_buffer, <insert stuff>, 
%        fit_window(max_window_size, get_blocal("is_popup", 0));
%     )
%   - closing with close_buffer closes the popup window as well.
% 
% The blocal variable is_popup marks a buffer as "popup buffer". 
% It contains the upper limit when fitting the window.
define popup_buffer() % (buf, max_rows = Max_Popup_Size)
{
   % get arguments
   variable buf, oldbuf, max_rows;
   (buf, max_rows) = push_defaults(whatbuf(), Max_Popup_Size, _NARGS);
   % currently open windows
   variable open_windows = nwindows() - MINIBUFFER_ACTIVE;
   % go to the buffer
   oldbuf = pop2buf_whatbuf(buf);
   define_blocal_var ("oldbuf", oldbuf);
   % reduce open_windows if replacing a resizable popup
   sw2buf(oldbuf);
   if (get_blocal("is_popup", 0) != 0)
     open_windows--;
   sw2buf(buf);
   
   % do not fit window if this might do harm to current setting
   if (open_windows > 1)
     max_rows = 0;      
     
   define_blocal_var("is_popup", max_rows);
   !if(bobp and eobp) % not empty
     fit_window(max_rows);
}

% --- push_keymap/pop_keymap ---------------------------------------------

static variable stack_name = "keymap_stack";

% temporarily push the keymap  
define push_keymap(new_keymap)
{
   !if (blocal_var_exists (stack_name))
     define_blocal_var (stack_name, "");

   set_blocal_var(what_keymap()+"|"+get_blocal_var(stack_name), stack_name);
   
   use_keymap(new_keymap);
   variable mode, flag;
   (mode, flag) = what_mode();
   set_mode(mode + " (" + new_keymap + ")", flag);
   %Test show("keymap stack is:", get_blocal_var(stack_name));
   %Test show("current keymap is:", what_keymap());
}

define pop_keymap ()
{
   variable kstack = get_blocal_var(stack_name);
   variable oldmap = extract_element (kstack, 0, '|');
   if (oldmap == "")
     error("keymap stack is empty.");
   
   variable mode, flag;
   (mode, flag) = what_mode();
   set_mode(mode[[0:-(strlen(what_keymap)+4)]], flag);
   use_keymap(oldmap);
   set_blocal_var(kstack[[strlen(oldmap)+1:]], stack_name);
  %Test show("keymap stack is:", get_blocal_var(stack_name));
  %Test	show("current keymap is:", what_keymap());
}

% Rebind all keys that are bound to old_fun to new_fun
define rebind() % (old_fun, new_fun, keymap=what_keymap(), key_prefix="")
{
   variable old_fun, new_fun, keymap, key_prefix;
   (old_fun, new_fun, keymap, key_prefix) = 
     push_defaults( , , what_keymap(),"", _NARGS);
   
   variable key;
   loop (which_key (old_fun))
   {
      key = ();
      definekey(new_fun, key_prefix + key, keymap);
   }
}

% Make a binding for new_fun for all bindings to old_fun
% prepending the _Reserved_Key_Prefix
define rebind_reserved(old_fun, new_fun, keymap)
{
   rebind(old_fun, new_fun, keymap, _Reserved_Key_Prefix);
}

% -----------------------------------------------------------------------

% Read a file and return it as string
define strread_file(name)
{
   variable fp = fopen (name, "r");
   variable line, str = "";
   if (fp == NULL) verror ("File %s not found", name);
   while (-1 != fgets (&line, fp))
     str += line;
   () = fclose (fp);
   return str;
}

% restore (or update, if file changed on disk) a buffer to the file version
public define restore_buffer()
{
   variable file = buffer_filename();
   variable col = what_column(), line = what_line();

   if(file_status(file) != 1) 
     return message("cannot open " + file);
   delbuf(whatbuf());
   () = find_file(file);
   goto_line(line);
   goto_column(col);
   % turn off the "changed on disk" bit
   % cf. example set_overwrite_mode () in the setbuf_info help
   setbuf_info (getbuf_info () & ~0x004);  
}

% --- Buffer local hooks  -----------------------------------------------
% 
% Tools for the definition and use of buffer local hooks -- just like the
% indent_hook or the newline_and_indent_hook jed already provides.
% Extend this idea to additional hooks that can be set by a mode and used by 
% another. Allows customizatin to be split in the "language" mode that 
% provides functionality and the "emulation" mode that does the keybinding.
% 
% Implementation is done via blocal vars. The hook can either be given as
% a pointer (reference) to a function of as the function name as string.

%!%+
%\function{run_blocal_hook}
%\synopsis{Run a blocal hook if it exists}
%\usage{ Void run_blocal_hook(String hook, [args])}
%\description
%   Run a blocal hook if it exists
%\example
%#v+
% define run_buffer()
% {
%    run_blocal_hook("run_buffer_hook");
% }
%#v-
%\seealso{runhooks, run_function, get_blocal, get_blocal_var}
%!%-
define run_blocal_hook() % (hook, [args])       
{	
   variable args = __pop_args(_NARGS-1);
   variable hook = ();
   () = run_function(get_blocal(hook, NULL), __push_args(args));
}

%!%+
%\function{run_buffer}
%\synopsis{Evaluate the current buffer as script}
%\usage{ Void run_buffer()}
%\description
%  Evaluate the current buffer as script, using the blocal_hook 
%  "run_buffer_hook" to find out which function to use. This way
%  a mode for a scriptiong language can set the right function but leave
%  a unified keybinding up to the emulation mode (or your .jedrc)
%\example
%  Up to date modes set the blocal var by themself, e.g.
%#v+
% public define gnuplot_mode ()
% {
%    set_mode(modename, 4);
%    use_syntax_table (modename);
%    use_keymap ("GnuplotMap");
%    mode_set_mode_info (modename, "fold_info", "#{{{\r#}}}\r\r");
%    mode_set_mode_info (modename, "init_mode_menu", &init_mode_menu);
%    define_blocal_var("help_for_word_hook", &gnuplot_help);
%    define_blocal_var("run_buffer_hook", &gnuplot_run);
%    run_mode_hooks("gnuplot_mode_hook");
% }
%#v-
%  For others you can do it using the mode_hooks, e.g.
%#v+
%   define latex_mode_hook ()
% {
%    define_blocal_var("run_buffer_hook", "latex_compose");
% }
% 
% define calc_mode_hook ()
% {
%    define_blocal_var("run_buffer_hook", "calc_make_calculation");
%    set_buffer_undo(1);
% }
%#v-
%\seealso{run_blocal_hook, evalbuf}
%!%-
public define run_buffer()
{
   run_blocal_hook("run_buffer_hook");
}

% Convert the modename to a canonic form (the donwcased first part)
% This can be used for mode-dependend help, variables, ...
define normalized_modename() % (mode=get_mode_name)
{
   variable mode;
   mode = push_defaults(get_mode_name, _NARGS);
   mode = extract_element (mode, 0, ' ');
   if (mode == "") 
     mode = "no";
   return strlow (mode);
}

% A help string for modes to be shown on the message-line
variable Help_Message = Assoc_Type[String_Type, "no help available"];

% Set the mode-dependend string with help (e.g. on keybindings)
define set_help_message(str)
{
   Help_Message[normalized_modename] = str;
}


% Show a mode-dependend string with help (e.g. on keybindings)
define help_message()
{
  message(Help_Message[normalized_modename]);
}


provide("bufutils");

[2003 date index] [2003 thread index]
[Thread Prev] [Thread Next]      [Date Prev] [Date Next]