[BACK]Return to help.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / vim

File: [local] / src / usr.bin / vim / Attic / help.c (download)

Revision 1.1, Sat Sep 7 21:40:26 1996 UTC (27 years, 9 months ago) by downsj
Branch: MAIN

Initial revision

/*	$OpenBSD: help.c,v 1.1 1996/09/07 21:40:26 downsj Exp $	*/
/* vi:set ts=4 sw=4:
 *
 * VIM - Vi IMproved		by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 */

/*
 * help.c: open a read-only window on the vim_help.txt file
 */

#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"

	void
do_help(arg)
	char_u		*arg;
{
	char_u	*fnamep;
	FILE	*helpfd;			/* file descriptor of help file */
	int		n;
	WIN		*wp;
	int		num_matches;
	char_u	**matches;
	int		need_free = FALSE;

	/*
	 * If an argument is given, check if there is a match for it.
	 */
	if (*arg != NUL)
	{
		n = find_help_tags(arg, &num_matches, &matches);
		if (num_matches == 0 || n == FAIL)
		{
			EMSG2("Sorry, no help for %s", arg);
			return;
		}

		/* The first file is the best match */
		arg = strsave(matches[0]);
		need_free = TRUE;
		FreeWild(num_matches, matches);
	}

	/*
	 * If there is already a help window open, use that one.
	 */
	if (!curwin->w_buffer->b_help)
	{
		for (wp = firstwin; wp != NULL; wp = wp->w_next)
			if (wp->w_buffer != NULL && wp->w_buffer->b_help)
				break;
		if (wp != NULL && wp->w_buffer->b_nwindows > 0)
			win_enter(wp, TRUE);
		else
		{
			/*
			 * There is no help buffer yet.
			 * Try to open the file specified by the "helpfile" option.
			 */
			fnamep = p_hf;
			if ((helpfd = fopen((char *)p_hf, READBIN)) == NULL)
			{
#if defined(MSDOS)
			/*
			 * for MSDOS: try the DOS search path
			 */
				fnamep = searchpath("vim_help.txt");
				if (fnamep == NULL ||
							(helpfd = fopen((char *)fnamep, READBIN)) == NULL)
				{
					smsg((char_u *)"Sorry, help file \"%s\" and \"vim_help.txt\" not found", p_hf);
					goto erret;
				}
#else
				smsg((char_u *)"Sorry, help file \"%s\" not found", p_hf);
				goto erret;
#endif
			}
			fclose(helpfd);

			if (win_split(0, FALSE) == FAIL)
				goto erret;
			
			if (curwin->w_height < p_hh)
				win_setheight((int)p_hh);

#ifdef RIGHTLEFT
			curwin->w_p_rl = 0;				/* help window is left-to-right */
#endif
			curwin->w_p_nu = 0;				/* no line numbers */

			/*
			 * open help file (do_ecmd() will set b_help flag, readfile() will
			 * set b_p_ro flag)
			 */
			(void)do_ecmd(0, fnamep, NULL, NULL, TRUE, (linenr_t)0, TRUE);

			/* save the values of the options we change */
			vim_free(help_save_isk);
			help_save_isk = strsave(curbuf->b_p_isk);
			help_save_ts = curbuf->b_p_ts;

			/* accept all chars for keywords, except ' ', '*', '"', '|' */
			set_string_option((char_u *)"isk", -1,
											 (char_u *)"!-~,^*,^|,^\"", TRUE);
			curbuf->b_p_ts = 8;
			check_buf_options(curbuf);
			(void)init_chartab();		/* needed because 'isk' changed */
		}
	}

	restart_edit = 0;		/* don't want insert mode in help file */

	stuffReadbuff((char_u *)":ta ");
	if (arg != NULL && *arg != NUL)
		stuffReadbuff(arg);
	else
		stuffReadbuff((char_u *)"vim_help.txt");		/* go to the index */
	stuffcharReadbuff('\n');

erret:
	if (need_free)
		vim_free(arg);
}

/*
 * Return a heuristic indicating how well the given string matches.  The
 * smaller the number, the better the match.  This is the order of priorities,
 * from best match to worst match:
 *		- Match with least alpha-numeric characters is better.
 *		- Match with least total characters is better.
 *		- Match towards the start is better.
 * Assumption is made that the matched_string passed has already been found to
 * match some string for which help is requested.  webb.
 */
	int
help_heuristic(matched_string, offset)
	char_u	*matched_string;
	int		offset;				/* offset for match */
{
	int		num_letters;
	char_u	*p;

	num_letters = 0;
	for (p = matched_string; *p; p++)
		if (isalnum(*p))
			num_letters++;

	/*
	 * Multiply the number of letters by 100 to give it a much bigger
	 * weighting than the number of characters.
	 * If the match starts in the middle of a word, add 10000 to put it
	 * somewhere in the last half.
	 * If the match is more than 2 chars from the start, multiply by 200 to
	 * put it after matches at the start.
	 */
	if (isalnum(matched_string[offset]) && offset > 0 &&
										  isalnum(matched_string[offset - 1]))
		offset += 10000;
	else if (offset > 2)
		offset *= 200;
	return (int)(100 * num_letters + STRLEN(matched_string) + offset);
}

static int help_compare __ARGS((const void *s1, const void *s2));

/*
 * Compare functions for qsort() below, that checks the help heuristics number
 * that has been put after the tagname by find_tags().
 */
	static int
help_compare(s1, s2)
	const void	*s1;
	const void	*s2;
{
	char	*p1;
	char	*p2;

	p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
	p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
	return strcmp(p1, p2);
}

/*
 * Find all help tags matching "arg", sort them and return in matches[], with
 * the number of matches in num_matches.
 * We try first with case, and then ignoring case.  Then we try to choose the
 * "best" match from the ones found.
 */
	int
find_help_tags(arg, num_matches, matches)
	char_u		*arg;
	int			*num_matches;
	char_u		***matches;
{
	char_u	*s, *d;
	regexp	*prog;
	int		attempt;
	int		retval = FAIL;

	reg_magic = p_magic;
	d = IObuff;				/* assume IObuff is long enough! */

	/*
	 * Replace "|" with "bar", """ with "quote" and "*" with "star" to
	 * match the name of the tags for these commands.
	 * Replace "*" with ".*" and "?" with "." to match command line
	 * completion.
	 * Insert a backslash before '~', '$' and '.' to avoid their
	 * special meaning.
	 * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
	 * ":help i_^_CTRL-D" work.
	 * If tag starts with ', toss everything after a second '. Fixes
	 * CTRL-] on 'option'. (would include the trailing '.').
	 */
	if (STRCMP(arg, "*") == 0 || STRCMP(arg, "[*") == 0 ||
											   STRCMP(arg, "]*") == 0)
	{
		if (*arg != '*')
			*d++ = *arg;
		STRCPY(d, "star");
		d += 4;
	}
	else
	{
		for (s = arg; *s; ++s)
		{
			if (d - IObuff > IOSIZE - 10)		/* getting too long!? */
				break;
			switch (*s)
			{
				case '|':	STRCPY(d, "bar");
							d += 3;
							continue;
				case '\"':	STRCPY(d, "quote");
							d += 5;
							continue;
				case '*':	*d++ = '.';
							break;
							/* "?", ":?" and "?<CR>" are real tags */
				case '?':	if (arg[1] == NUL ||
											 STRCMP(arg, ":?") == 0 ||
											STRCMP(arg, "?<CR>") == 0)
								break;
							*d++ = '.';
							continue;
				case '$':
				case '.':
				case '~':	*d++ = '\\';
							break;
			}
			if (*s < ' ' || (*s == '^' && s[1] && s[1] != '_'))	/* ^x */
			{
				STRCPY(d, "CTRL-");
				d += 5;
				if (*s < ' ')
				{
					*d++ = *s + '@';
					continue;
				}
				++s;
			}
			else if (*s == '^')			/* "^" or "CTRL-^" or "^_" */
				*d++ = '\\';
			*d++ = *s;
			if (*s == '\'' && s > arg && *arg == '\'')
				break;
		}
	}
	*d = NUL;

	reg_ic = FALSE;
	prog = vim_regcomp(IObuff);
	if (prog == NULL)
		return FAIL;

	/* First try to match with case, then without */
	for (attempt = 0; attempt < 2; ++attempt, reg_ic = TRUE)
	{
		*matches = (char_u **)"";
		*num_matches = 0;
		retval = find_tags(NULL, prog, num_matches, matches, TRUE);
		if (retval == FAIL || *num_matches)
			break;
	}
	vim_free(prog);

#ifdef HAVE_QSORT
	/*
	 * Sort the matches found on the heuristic number that is after the
	 * tag name.  If there is no qsort, the output will be messy!
	 */
	qsort((void *)*matches, (size_t)*num_matches,
											  sizeof(char_u *), help_compare);
#endif
	return OK;
}