[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.1.1.1

1.1       downsj      1: /* $OpenBSD$   */
                      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:
                     43:        /* The first file is the best match */
                     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:             */
                    101:            (void)do_ecmd(0, fnamep, NULL, NULL, TRUE, (linenr_t)0, TRUE);
                    102:
                    103:            /* save the values of the options we change */
                    104:            vim_free(help_save_isk);
                    105:            help_save_isk = strsave(curbuf->b_p_isk);
                    106:            help_save_ts = curbuf->b_p_ts;
                    107:
                    108:            /* accept all chars for keywords, except ' ', '*', '"', '|' */
                    109:            set_string_option((char_u *)"isk", -1,
                    110:                                             (char_u *)"!-~,^*,^|,^\"", TRUE);
                    111:            curbuf->b_p_ts = 8;
                    112:            check_buf_options(curbuf);
                    113:            (void)init_chartab();       /* needed because 'isk' changed */
                    114:        }
                    115:    }
                    116:
                    117:    restart_edit = 0;       /* don't want insert mode in help file */
                    118:
                    119:    stuffReadbuff((char_u *)":ta ");
                    120:    if (arg != NULL && *arg != NUL)
                    121:        stuffReadbuff(arg);
                    122:    else
                    123:        stuffReadbuff((char_u *)"vim_help.txt");        /* go to the index */
                    124:    stuffcharReadbuff('\n');
                    125:
                    126: erret:
                    127:    if (need_free)
                    128:        vim_free(arg);
                    129: }
                    130:
                    131: /*
                    132:  * Return a heuristic indicating how well the given string matches.  The
                    133:  * smaller the number, the better the match.  This is the order of priorities,
                    134:  * from best match to worst match:
                    135:  *     - Match with least alpha-numeric characters is better.
                    136:  *     - Match with least total characters is better.
                    137:  *     - Match towards the start is better.
                    138:  * Assumption is made that the matched_string passed has already been found to
                    139:  * match some string for which help is requested.  webb.
                    140:  */
                    141:    int
                    142: help_heuristic(matched_string, offset)
                    143:    char_u  *matched_string;
                    144:    int     offset;             /* offset for match */
                    145: {
                    146:    int     num_letters;
                    147:    char_u  *p;
                    148:
                    149:    num_letters = 0;
                    150:    for (p = matched_string; *p; p++)
                    151:        if (isalnum(*p))
                    152:            num_letters++;
                    153:
                    154:    /*
                    155:     * Multiply the number of letters by 100 to give it a much bigger
                    156:     * weighting than the number of characters.
                    157:     * If the match starts in the middle of a word, add 10000 to put it
                    158:     * somewhere in the last half.
                    159:     * If the match is more than 2 chars from the start, multiply by 200 to
                    160:     * put it after matches at the start.
                    161:     */
                    162:    if (isalnum(matched_string[offset]) && offset > 0 &&
                    163:                                          isalnum(matched_string[offset - 1]))
                    164:        offset += 10000;
                    165:    else if (offset > 2)
                    166:        offset *= 200;
                    167:    return (int)(100 * num_letters + STRLEN(matched_string) + offset);
                    168: }
                    169:
                    170: static int help_compare __ARGS((const void *s1, const void *s2));
                    171:
                    172: /*
                    173:  * Compare functions for qsort() below, that checks the help heuristics number
                    174:  * that has been put after the tagname by find_tags().
                    175:  */
                    176:    static int
                    177: help_compare(s1, s2)
                    178:    const void  *s1;
                    179:    const void  *s2;
                    180: {
                    181:    char    *p1;
                    182:    char    *p2;
                    183:
                    184:    p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
                    185:    p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
                    186:    return strcmp(p1, p2);
                    187: }
                    188:
                    189: /*
                    190:  * Find all help tags matching "arg", sort them and return in matches[], with
                    191:  * the number of matches in num_matches.
                    192:  * We try first with case, and then ignoring case.  Then we try to choose the
                    193:  * "best" match from the ones found.
                    194:  */
                    195:    int
                    196: find_help_tags(arg, num_matches, matches)
                    197:    char_u      *arg;
                    198:    int         *num_matches;
                    199:    char_u      ***matches;
                    200: {
                    201:    char_u  *s, *d;
                    202:    regexp  *prog;
                    203:    int     attempt;
                    204:    int     retval = FAIL;
                    205:
                    206:    reg_magic = p_magic;
                    207:    d = IObuff;             /* assume IObuff is long enough! */
                    208:
                    209:    /*
                    210:     * Replace "|" with "bar", """ with "quote" and "*" with "star" to
                    211:     * match the name of the tags for these commands.
                    212:     * Replace "*" with ".*" and "?" with "." to match command line
                    213:     * completion.
                    214:     * Insert a backslash before '~', '$' and '.' to avoid their
                    215:     * special meaning.
                    216:     * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
                    217:     * ":help i_^_CTRL-D" work.
                    218:     * If tag starts with ', toss everything after a second '. Fixes
                    219:     * CTRL-] on 'option'. (would include the trailing '.').
                    220:     */
                    221:    if (STRCMP(arg, "*") == 0 || STRCMP(arg, "[*") == 0 ||
                    222:                                               STRCMP(arg, "]*") == 0)
                    223:    {
                    224:        if (*arg != '*')
                    225:            *d++ = *arg;
                    226:        STRCPY(d, "star");
                    227:        d += 4;
                    228:    }
                    229:    else
                    230:    {
                    231:        for (s = arg; *s; ++s)
                    232:        {
                    233:            if (d - IObuff > IOSIZE - 10)       /* getting too long!? */
                    234:                break;
                    235:            switch (*s)
                    236:            {
                    237:                case '|':   STRCPY(d, "bar");
                    238:                            d += 3;
                    239:                            continue;
                    240:                case '\"':  STRCPY(d, "quote");
                    241:                            d += 5;
                    242:                            continue;
                    243:                case '*':   *d++ = '.';
                    244:                            break;
                    245:                            /* "?", ":?" and "?<CR>" are real tags */
                    246:                case '?':   if (arg[1] == NUL ||
                    247:                                             STRCMP(arg, ":?") == 0 ||
                    248:                                            STRCMP(arg, "?<CR>") == 0)
                    249:                                break;
                    250:                            *d++ = '.';
                    251:                            continue;
                    252:                case '$':
                    253:                case '.':
                    254:                case '~':   *d++ = '\\';
                    255:                            break;
                    256:            }
                    257:            if (*s < ' ' || (*s == '^' && s[1] && s[1] != '_')) /* ^x */
                    258:            {
                    259:                STRCPY(d, "CTRL-");
                    260:                d += 5;
                    261:                if (*s < ' ')
                    262:                {
                    263:                    *d++ = *s + '@';
                    264:                    continue;
                    265:                }
                    266:                ++s;
                    267:            }
                    268:            else if (*s == '^')         /* "^" or "CTRL-^" or "^_" */
                    269:                *d++ = '\\';
                    270:            *d++ = *s;
                    271:            if (*s == '\'' && s > arg && *arg == '\'')
                    272:                break;
                    273:        }
                    274:    }
                    275:    *d = NUL;
                    276:
                    277:    reg_ic = FALSE;
                    278:    prog = vim_regcomp(IObuff);
                    279:    if (prog == NULL)
                    280:        return FAIL;
                    281:
                    282:    /* First try to match with case, then without */
                    283:    for (attempt = 0; attempt < 2; ++attempt, reg_ic = TRUE)
                    284:    {
                    285:        *matches = (char_u **)"";
                    286:        *num_matches = 0;
                    287:        retval = find_tags(NULL, prog, num_matches, matches, TRUE);
                    288:        if (retval == FAIL || *num_matches)
                    289:            break;
                    290:    }
                    291:    vim_free(prog);
                    292:
                    293: #ifdef HAVE_QSORT
                    294:    /*
                    295:     * Sort the matches found on the heuristic number that is after the
                    296:     * tag name.  If there is no qsort, the output will be messy!
                    297:     */
                    298:    qsort((void *)*matches, (size_t)*num_matches,
                    299:                                              sizeof(char_u *), help_compare);
                    300: #endif
                    301:    return OK;
                    302: }