The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Поиск:  Каталог документации

Next Previous Contents

14. Build Your "WYSIWYG" HTML Editor With Vi & Netscape

This section was written by Manas K Laha , Aerospace Engineering Department, IIT Kharagpur, India. This is about a quick and dirty way to create an HTML editor combining vi and Netscape.

If vi (or one of its friends, such as elvis or vim ) is your favorite text editor, as it is mine, you must surely long for a way of creating HTML with it quickly and comfortably. And if with that you could get the convenience of "WYSIWYG", wouldn't you just jump at it? All this is indeed possible, and here we'll see how.

The major hurdles I've found in editing HTML with vi are

  1. The need to write HTML tags. It appears that there are more tags to be written than displayable matter. Moreover, some of these tags have a syntax that is hard to remember.
  2. The need to keep track of whether an opening tag has been given its proper closing tag at the right place (for example, whether a < ol> has a matching < /ol> ).
  3. Difficulty in readily identifying matching pairs of tags.

These can be got around using some of the less used features of vi and friends. In this article I shall use vim for definiteness, but the ideas should apply to classic vi and its other look-alikes as well.

The "abbreviation" feature of vim:

Vim has a feature whereby it is possible to assign a keystroke sequence to represent a string of characters in input mode. This is the ab colon command. For example, the command

:ab tT <tt> </tt>

creates an abbreviation, named tT, for the sequence of characters < tt>< /tt>. Then, in input mode, as soon as the characters tT are typed, they are replaced by the string < tt>< /tt>. Complicated HTML tags may also be abbreviated. The ab definition

:ab aH <a href=?http://?><!? { ?>^M
Comments here^M^D </a><!? } ?>

makes aH the shorthand for

<a href=?http://?><!? { ?>
Comments here
</a><!? } ?>

where the two ^M s cause the two line breaks and the ^D causes the closing < /a> tag to be indented back to be in line with the opening < a> tag. (Indenting the matter enclosed within a matching pair of tags makes reading and editing the raw HTML easier.) The syntax of the tag is outlined, as an aid to memory. The 'Comments here' line is a placeholder, to be replaced with appropriate text.

What are the { and } within HTML comments doing there? Aha! Those are for matching the opening and closing tags (in this case < a> and < /a>). The bracket matching feature of vi (using the % key) is readily usable for the purpose. This can be really helpful when the opening and closing tags are many lines apart and cannot be readily matched 'by eye', such as can be the case with the < ol>< /ol> pair.

This technique can be extended to generate fancier HTML, for example with frames. All one has to do is to define the appropriate abbreviations. For example, the definition

:ab fS <frameset scrolling=?no?
frameborder=?0? framespacing=?0?
cols=?20%,80%?><!? { ?>^M </frameset> <!?
} ?>

makes the string fS a convenient abbreviation for the pair of tags:

<frameset scrolling=?no? frameborder=?0?
framespacing=?0? cols=?20%,80%?> <!? { ?>
</frameset><!? } ?>

Some tags do not like comments to come in between the opening and closing pair. The ones I have found are < title>< /title> and < a\ href=?mailto:?> < /a>. Luckily, in both these the opening and closing pair are never very far apart, so the braces-within-comments feature is not needed.

14.1 Sample .vimhtmlrc File

How do I tell vim about these abbreviations? I put all of them (and a command to set some of vim's variables) in a file, which I call .vimhtmlrc and which resides in my home directory, and invoke

vim -u ~/.vimhtmlrc index.html

where index.html is the HTML file I want to edit. This is what my .vimhtmlrc file looks like:

ab aH <a href=?http://?><!? { ?>^M
Comments here^M^D</a> <!? } ?>

ab aM <a href=?mailto:?>^M Comments
here^M^D </a>

ab bO <body bgcolor=#e0e0e0
text=#000000><!? { ?>^M </body> <!? } ?>

ab bR <br>

ab cE <center bgcolor=#e0e0e0
text=#000000><!? { ?>^M </center> <!? } ?>

ab cM <!? ^M ?>

ab cO <code> <!? { ?>^M </code> <!? } ?>

ab dL <dl><!? { ?>^M</dl><!? } ?>

ab dT <dt>

ab fO <font color=#000000>^M </font>

ab h1 <h1><!? { ?>^M Heading size
1^M^D </h1> <!? } ?>

ab h2 <h2><!? { ?>^M Heading size
2^M^D </h2><!? } ?>

ab h3 <h3><!? { ?>^M Heading size
3^M^D </h3> <!? } ?>

ab hD <head> <!? { ?>^M </head> <!? } ?>

ab hR <hr>

ab hT <html> <!? { ?>^M </html> <!? } ?>

ab iM <img src=??>

ab lI <li> <!? { ?>^M </li> <!? } ?>

ab oL <ol> <!? { ?>^M </ol> <!? } ?>

ab pR <pre> <!? { ?>^M </pre> <!? } ?>

ab tD <td> <!? { ?>^M </td> <!? } ?>

ab tL  <title>^M Title here^M^D </title>

ab tS <table bgcolor=?#d0d0d0?> <!? {
?>^M </table> <!? } ?>

ab tT <tt> </tt>

ab uL <ul> <!? { ?>^M </ul> <!? } ?>

ab xB  <b> </b>

ab xI  <i> </i>

ab xP <p> <!? { ?>^M </p> <!? } ?>
se ai aw sw=4 ts=4 wm=10 showmode
showmatch ruler magic

When the ab commands are put in a file, to be read in by vim at startup, then the leading :is not needed. The last line is a command to set some of vim 's variables. Here is what they mean:

se        set: tells vim to activate the options that follow autoindent: begin the next
ai        line in the same column as this one (and not from column 1)
aw        autowrite: automatically write file to disk when it changes on a TAB key, move cursor 4
ts=4      characters (and not the normal 8); this is my personal preference
sw=4      number of spaces to use for indentation chars from right margin where
wm=10     line wrapping starts (useful if one is writing running text and not programs)
                  message on status line to show
showmode  current mode (for the novice, actually) briefly jump to matching 
                  opening '(' or '{' or '[' as
showmatch soon as a closing ')' or '}' or ']' is typed; beep if no match
ruler     show cursor line and column in status line some characters, such as '.'
magic     and '*', have special meanings in search and replace patterns.

Typing help in a vim window shows the explanations for these options and many more besides.


'WYSIWYG' has two parts to it. To begin with is the fact that Netscape under Unix (and Linux) can be controlled remotely.

That is, you may control the behavior of an already running Netscape through commands of the form

netscape -remote -noraise 'openFile(/home/mlaha/html/index.html)'

(If no Netscape is running, the command just exits with an error message.) This command causes the Netscape browser window to attempt to open the file /home/mlaha/html/index.html. For more on remote controlling Netscape, see

And then, there is atchange . Jeffrey Copeland and Jeffrey Haemer ( , and ) describe a little shell script, called atchange, that waits in the background for a named file to change and then invokes a specified command. Thus,

atchange index.html 'netscape -noraise -remote 'openFile(/home/mlaha/html/index.html)'' &

would cause atchange to run in the background, watching the file index.html and, as soon as it changed, ask Netscape to display it afresh. If you were editing index.html with vi, then, when you saved it (with :w, say), atchange would spring into action and Netscape would update its display.

If you wish to edit another HTML file, you have to quit vim, kill the current invocation of atchange, then start it again with the name of the new file in place of index.html and begin editing that file with vim.

14.3 Other 'WYSIWYG' uses

As you may have guessed, atchange can be used in other instances, too. You can make a handy 'WYSIWYG' LaTeX editor by having atchange monitor your LaTeX source and, when it changed, run the necessary programs to convert it to Postscript. The 'WYSIWYG' capability is provided in this case by invoking Ghostview with the monitoring option (-watch) that causes it to redisplay its current Postscript file whenever that file changes. Thus, every time you saved your LaTeX source file in the editor, the Postscript output with the latest changes would be automatically displayed in the Ghostscript window.

14.4 Source code for atchange

# by Jeff Haemer
#       and a tip o' the hat to Tom Schneider
#       who wrote the original version as a shell script

# version = 2.07 of atchange 1999 Dec 30

# 1999 Dec 18:  Added shell call to /bin/csh so that
# atchange works under Linux.

# 1999 Feb 5: By setting the PERLCSH variable, the new shell can tell
#             it has been called by atchange.
# The test inside the .cshrc is:
#if ( (! $?PERLCSH ) && $?prompt) then 
#   stty erase '^H'
#   set prompt = "`uname -n` \!% "
# This is necessary under Sun Solaris 2.6 because otherwise the
# call to stty gives an error message now.

# previous change: 1997 Jan 9
# delay time is 0.25 seconds

#  For current version and other information about this program, see:

#  Tom Schneider
#  National Cancer Institute
#  Laboratory of Mathematical Biology
#  Frederick, Maryland  21702-1201

# 1999 Dec 30:  James Haefner (
# has found that some changes are needed to make atchange
# work under Linux.  See the web site for details.
# This code will be revised when a good solution is found.

$0 =~ s(.*/)();                 # basename
$usage = "usage: $0 filename cmd | $0 command_file";
@ARGV || die $usage;            # check for proper invocation

# This allows the .cshrc to know that atchange has called it:

# Haefner Suggestion 1999 Dec 18:
##if default SHELL is sh or csh or tcsh use the following line
###$shell = $ENV{"SHELL"} ? $ENV{"SHELL"} : "/bin/sh";
##if default SHELL is bash (eg, Linux) use the following line
# 1999 Dec 28 - this is not a good idea - untestable by me
# $shell = "/bin/csh";

$shell = $ENV{"SHELL"} ? $ENV{"SHELL"} : "/bin/sh";
open(SHELL, "|$shell") || die "Can't pipe to $shell: $!";
select(SHELL); $| = 1;

if (@ARGV > 1) {                # it's a file and a command
        $file = shift;                          # peel off the filename
        $cmd{$file} = join(" ", @ARGV) . "\n";  #       and the command
        $old{$file} = (stat($file))[9]; # mod time.
} else {                        # it's a program
        open(PGM, shift) || die "Can't open $_: $!";
        $/ = "";                        # paragraph mode
        while(<PGM>) {                  # first read the program
                ($file, $cmd) = /(\S*)\s+([^\000]+)/;
                $cmd{$file} = $cmd;
                unless ($file) { print $cmd{$file}; next; }
                if ($file && ! $cmd{$file}) { warn "odd line"; next; };
                $old{$file} = (stat($file))[9]; # mod time.

while(1) {
        # sleep 1;              # wait a second, then
        select(undef, undef, undef, 0.25); # wait a quarter second, then
        foreach (keys %cmd) {   #       rip through the whole list

sub atchange {          # if $file has changed, do $cmd{$file}
        my($file) = @_;

        $new = (stat($file))[9];
        return 0 if ($old{$file} == $new);
        while (1) {                     # wait until it stops changing
                $old{$file} = $new;
                sleep 1;
                $new = (stat($file))[9];
                if ($old{$file} == $new) {
                        print $cmd{$file};
                        return 1;

Next Previous Contents

Inferno Solutions
Hosting by

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру