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

Annotation of src/usr.bin/vim/help.c, Revision 1.2

1.2     ! downsj      1: /* $OpenBSD: help.c,v 1.1.1.1 1996/09/07 21:40:26 downsj Exp $ */
1.1       downsj      2: /* vi:set ts=4 sw=4:
                      3:  *
                      4:  * VIM - Vi IMproved       by Bram Moolenaar
                      5:  *
                      6:  * Do ":help uganda"  in Vim to read copying and usage conditions.
                      7:  * Do ":help credits" in Vim to see a list of people who contributed.
                      8:  */
                      9:
                     10: /*
                     11:  * help.c: open a read-only window on the vim_help.txt file
                     12:  */
                     13:
                     14: #include "vim.h"
                     15: #include "globals.h"
                     16: #include "proto.h"
                     17: #include "option.h"
                     18:
                     19:    void
                     20: do_help(arg)
                     21:    char_u      *arg;
                     22: {
                     23:    char_u  *fnamep;
                     24:    FILE    *helpfd;            /* file descriptor of help file */
                     25:    int     n;
                     26:    WIN     *wp;
                     27:    int     num_matches;
                     28:    char_u  **matches;
                     29:    int     need_free = FALSE;
                     30:
                     31:    /*
                     32:     * If an argument is given, check if there is a match for it.
                     33:     */
                     34:    if (*arg != NUL)
                     35:    {
                     36:        n = find_help_tags(arg, &num_matches, &matches);
                     37:        if (num_matches == 0 || n == FAIL)
                     38:        {
                     39:            EMSG2("Sorry, no help for %s", arg);
                     40:            return;
                     41:        }
                     42:
1.2     ! downsj     43:        /* The first match is the best match */
1.1       downsj     44:        arg = strsave(matches[0]);
                     45:        need_free = TRUE;
                     46:        FreeWild(num_matches, matches);
                     47:    }
                     48:
                     49:    /*
                     50:     * If there is already a help window open, use that one.
                     51:     */
                     52:    if (!curwin->w_buffer->b_help)
                     53:    {
                     54:        for (wp = firstwin; wp != NULL; wp = wp->w_next)
                     55:            if (wp->w_buffer != NULL && wp->w_buffer->b_help)
                     56:                break;
                     57:        if (wp != NULL && wp->w_buffer->b_nwindows > 0)
                     58:            win_enter(wp, TRUE);
                     59:        else
                     60:        {
                     61:            /*
                     62:             * There is no help buffer yet.
                     63:             * Try to open the file specified by the "helpfile" option.
                     64:             */
                     65:            fnamep = p_hf;
                     66:            if ((helpfd = fopen((char *)p_hf, READBIN)) == NULL)
                     67:            {
                     68: #if defined(MSDOS)
                     69:            /*
                     70:             * for MSDOS: try the DOS search path
                     71:             */
                     72:                fnamep = searchpath("vim_help.txt");
                     73:                if (fnamep == NULL ||
                     74:                            (helpfd = fopen((char *)fnamep, READBIN)) == NULL)
                     75:                {
                     76:                    smsg((char_u *)"Sorry, help file \"%s\" and \"vim_help.txt\" not found", p_hf);
                     77:                    goto erret;
                     78:                }
                     79: #else
                     80:                smsg((char_u *)"Sorry, help file \"%s\" not found", p_hf);
                     81:                goto erret;
                     82: #endif
                     83:            }
                     84:            fclose(helpfd);
                     85:
                     86:            if (win_split(0, FALSE) == FAIL)
                     87:                goto erret;
                     88:
                     89:            if (curwin->w_height < p_hh)
                     90:                win_setheight((int)p_hh);
                     91:
                     92: #ifdef RIGHTLEFT
                     93:            curwin->w_p_rl = 0;             /* help window is left-to-right */
                     94: #endif
                     95:            curwin->w_p_nu = 0;             /* no line numbers */
                     96:
                     97:            /*
                     98:             * open help file (do_ecmd() will set b_help flag, readfile() will
                     99:             * set b_p_ro flag)
                    100:             */
1.2     ! downsj    101:            (void)do_ecmd(0, fnamep, NULL, NULL, (linenr_t)0,
        !           102:                                                   ECMD_HIDE + ECMD_SET_HELP);
1.1       downsj    103:
                    104:            /* save the values of the options we change */
                    105:            vim_free(help_save_isk);
                    106:            help_save_isk = strsave(curbuf->b_p_isk);
                    107:            help_save_ts = curbuf->b_p_ts;
                    108:
                    109:            /* accept all chars for keywords, except ' ', '*', '"', '|' */
                    110:            set_string_option((char_u *)"isk", -1,
                    111:                                             (char_u *)"!-~,^*,^|,^\"", TRUE);
                    112:            curbuf->b_p_ts = 8;
                    113:            check_buf_options(curbuf);
                    114:            (void)init_chartab();       /* needed because 'isk' changed */
                    115:        }
                    116:    }
                    117:
                    118:    restart_edit = 0;       /* don't want insert mode in help file */
                    119:
                    120:    stuffReadbuff((char_u *)":ta ");
                    121:    if (arg != NULL && *arg != NUL)
                    122:        stuffReadbuff(arg);
                    123:    else
                    124:        stuffReadbuff((char_u *)"vim_help.txt");        /* go to the index */
                    125:    stuffcharReadbuff('\n');
                    126:
                    127: erret:
                    128:    if (need_free)
                    129:        vim_free(arg);
                    130: }
                    131:
                    132: /*
                    133:  * Return a heuristic indicating how well the given string matches.  The
                    134:  * smaller the number, the better the match.  This is the order of priorities,
                    135:  * from best match to worst match:
                    136:  *     - Match with least alpha-numeric characters is better.
                    137:  *     - Match with least total characters is better.
                    138:  *     - Match towards the start is better.
                    139:  * Assumption is made that the matched_string passed has already been found to
                    140:  * match some string for which help is requested.  webb.
                    141:  */
                    142:    int
                    143: help_heuristic(matched_string, offset)
                    144:    char_u  *matched_string;
                    145:    int     offset;             /* offset for match */
                    146: {
                    147:    int     num_letters;
                    148:    char_u  *p;
                    149:
                    150:    num_letters = 0;
                    151:    for (p = matched_string; *p; p++)
                    152:        if (isalnum(*p))
                    153:            num_letters++;
                    154:
                    155:    /*
                    156:     * Multiply the number of letters by 100 to give it a much bigger
                    157:     * weighting than the number of characters.
                    158:     * If the match starts in the middle of a word, add 10000 to put it
                    159:     * somewhere in the last half.
                    160:     * If the match is more than 2 chars from the start, multiply by 200 to
                    161:     * put it after matches at the start.
                    162:     */
                    163:    if (isalnum(matched_string[offset]) && offset > 0 &&
                    164:                                          isalnum(matched_string[offset - 1]))
                    165:        offset += 10000;
                    166:    else if (offset > 2)
                    167:        offset *= 200;
                    168:    return (int)(100 * num_letters + STRLEN(matched_string) + offset);
                    169: }
                    170:
                    171: static int help_compare __ARGS((const void *s1, const void *s2));
                    172:
                    173: /*
                    174:  * Compare functions for qsort() below, that checks the help heuristics number
                    175:  * that has been put after the tagname by find_tags().
                    176:  */
                    177:    static int
                    178: help_compare(s1, s2)
                    179:    const void  *s1;
                    180:    const void  *s2;
                    181: {
                    182:    char    *p1;
                    183:    char    *p2;
                    184:
                    185:    p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
                    186:    p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
                    187:    return strcmp(p1, p2);
                    188: }
                    189:
                    190: /*
                    191:  * Find all help tags matching "arg", sort them and return in matches[], with
                    192:  * the number of matches in num_matches.
                    193:  * We try first with case, and then ignoring case.  Then we try to choose the
                    194:  * "best" match from the ones found.
                    195:  */
                    196:    int
                    197: find_help_tags(arg, num_matches, matches)
                    198:    char_u      *arg;
                    199:    int         *num_matches;
                    200:    char_u      ***matches;
                    201: {
                    202:    char_u  *s, *d;
                    203:    regexp  *prog;
                    204:    int     attempt;
                    205:    int     retval = FAIL;
1.2     ! downsj    206:    int     i;
        !           207:    static char *(mtable[]) = {"*", "g*", "[*", "]*",
        !           208:                               "/*", "/\\*", "/\\(\\)",
        !           209:                               "?", ":?", "?<CR>"};
        !           210:    static char *(rtable[]) = {"star", "gstar", "[star", "]star",
        !           211:                               "/star", "/\\\\star", "/\\\\(\\\\)",
        !           212:                               "?", ":?", "?<CR>"};
1.1       downsj    213:
                    214:    reg_magic = p_magic;
                    215:    d = IObuff;             /* assume IObuff is long enough! */
                    216:
                    217:    /*
1.2     ! downsj    218:     * Recognize a few exceptions to the rule.  Some strings that contain '*'
        !           219:     * with "star".  Otherwise '*' is recognized as a wildcard.
1.1       downsj    220:     */
1.2     ! downsj    221:    for (i = sizeof(mtable) / sizeof(char *); --i >= 0; )
1.1       downsj    222:    {
1.2     ! downsj    223:        if (STRCMP(arg, mtable[i]) == 0)
        !           224:        {
        !           225:            STRCPY(d, rtable[i]);
        !           226:            break;
        !           227:        }
1.1       downsj    228:    }
1.2     ! downsj    229:
        !           230:    if (i < 0)      /* no match in table, replace single characters */
1.1       downsj    231:    {
                    232:        for (s = arg; *s; ++s)
                    233:        {
1.2     ! downsj    234:            /*
        !           235:             * Replace "|" with "bar" and """ with "quote" to match the name of
        !           236:             * the tags for these commands.
        !           237:             * Replace "*" with ".*" and "?" with "." to match command line
        !           238:             * completion.
        !           239:             * Insert a backslash before '~', '$' and '.' to avoid their
        !           240:             * special meaning.
        !           241:             */
1.1       downsj    242:            if (d - IObuff > IOSIZE - 10)       /* getting too long!? */
                    243:                break;
                    244:            switch (*s)
                    245:            {
                    246:                case '|':   STRCPY(d, "bar");
                    247:                            d += 3;
                    248:                            continue;
                    249:                case '\"':  STRCPY(d, "quote");
                    250:                            d += 5;
                    251:                            continue;
                    252:                case '*':   *d++ = '.';
                    253:                            break;
1.2     ! downsj    254:                case '?':   *d++ = '.';
1.1       downsj    255:                            continue;
                    256:                case '$':
                    257:                case '.':
                    258:                case '~':   *d++ = '\\';
                    259:                            break;
                    260:            }
1.2     ! downsj    261:
        !           262:            /*
        !           263:             * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
        !           264:             * ":help i_^_CTRL-D" work.
        !           265:             */
1.1       downsj    266:            if (*s < ' ' || (*s == '^' && s[1] && s[1] != '_')) /* ^x */
                    267:            {
                    268:                STRCPY(d, "CTRL-");
                    269:                d += 5;
                    270:                if (*s < ' ')
                    271:                {
                    272:                    *d++ = *s + '@';
                    273:                    continue;
                    274:                }
                    275:                ++s;
                    276:            }
                    277:            else if (*s == '^')         /* "^" or "CTRL-^" or "^_" */
                    278:                *d++ = '\\';
1.2     ! downsj    279:
        !           280:            /*
        !           281:             * Insert a backslash before a backslash after a slash, for search
        !           282:             * pattern tags: "/\|" --> "/\\|".
        !           283:             */
        !           284:            else if (s[0] == '\\' && s[1] != '\\' &&
        !           285:                                                  *arg == '/' && s == arg + 1)
        !           286:                *d++ = '\\';
        !           287:
1.1       downsj    288:            *d++ = *s;
1.2     ! downsj    289:
        !           290:            /*
        !           291:             * If tag starts with ', toss everything after a second '. Fixes
        !           292:             * CTRL-] on 'option'. (would include the trailing '.').
        !           293:             */
1.1       downsj    294:            if (*s == '\'' && s > arg && *arg == '\'')
                    295:                break;
                    296:        }
1.2     ! downsj    297:        *d = NUL;
1.1       downsj    298:    }
                    299:
                    300:    reg_ic = FALSE;
                    301:    prog = vim_regcomp(IObuff);
                    302:    if (prog == NULL)
                    303:        return FAIL;
                    304:
                    305:    /* First try to match with case, then without */
                    306:    for (attempt = 0; attempt < 2; ++attempt, reg_ic = TRUE)
                    307:    {
                    308:        *matches = (char_u **)"";
                    309:        *num_matches = 0;
1.2     ! downsj    310:        retval = find_tags(NULL, prog, num_matches, matches, TRUE, FALSE);
1.1       downsj    311:        if (retval == FAIL || *num_matches)
                    312:            break;
                    313:    }
                    314:    vim_free(prog);
                    315:
                    316: #ifdef HAVE_QSORT
                    317:    /*
                    318:     * Sort the matches found on the heuristic number that is after the
                    319:     * tag name.  If there is no qsort, the output will be messy!
                    320:     */
                    321:    qsort((void *)*matches, (size_t)*num_matches,
                    322:                                              sizeof(char_u *), help_compare);
                    323: #endif
                    324:    return OK;
                    325: }