- Subject: view mode for readonly buffers
- From: Günter Milde <milde@xxxxxxxxxxxxxxxxxxxx>
- Date: Fri, 2 May 2003 11:02:30 +0200
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]