home *** CD-ROM | disk | FTP | other *** search
/ vim.ftp.fu-berlin.de / 2015-02-03.vim.ftp.fu-berlin.de.tar / vim.ftp.fu-berlin.de / amiga / vim46src.lha / vim-4.6 / src / help.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-06  |  7.8 KB  |  325 lines

  1. /* vi:set ts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8.  
  9. /*
  10.  * help.c: open a read-only window on the vim_help.txt file
  11.  */
  12.  
  13. #include "vim.h"
  14. #include "globals.h"
  15. #include "proto.h"
  16. #include "option.h"
  17.  
  18.     void
  19. do_help(arg)
  20.     char_u        *arg;
  21. {
  22.     char_u    *fnamep;
  23.     FILE    *helpfd;            /* file descriptor of help file */
  24.     int        n;
  25.     WIN        *wp;
  26.     int        num_matches;
  27.     char_u    **matches;
  28.     int        need_free = FALSE;
  29.  
  30.     /*
  31.      * If an argument is given, check if there is a match for it.
  32.      */
  33.     if (*arg != NUL)
  34.     {
  35.         n = find_help_tags(arg, &num_matches, &matches);
  36.         if (num_matches == 0 || n == FAIL)
  37.         {
  38.             EMSG2("Sorry, no help for %s", arg);
  39.             return;
  40.         }
  41.  
  42.         /* The first match is the best match */
  43.         arg = strsave(matches[0]);
  44.         need_free = TRUE;
  45.         FreeWild(num_matches, matches);
  46.     }
  47.  
  48.     /*
  49.      * If there is already a help window open, use that one.
  50.      */
  51.     if (!curwin->w_buffer->b_help)
  52.     {
  53.         for (wp = firstwin; wp != NULL; wp = wp->w_next)
  54.             if (wp->w_buffer != NULL && wp->w_buffer->b_help)
  55.                 break;
  56.         if (wp != NULL && wp->w_buffer->b_nwindows > 0)
  57.             win_enter(wp, TRUE);
  58.         else
  59.         {
  60.             /*
  61.              * There is no help buffer yet.
  62.              * Try to open the file specified by the "helpfile" option.
  63.              */
  64.             fnamep = p_hf;
  65.             if ((helpfd = fopen((char *)p_hf, READBIN)) == NULL)
  66.             {
  67. #if defined(MSDOS)
  68.             /*
  69.              * for MSDOS: try the DOS search path
  70.              */
  71.                 fnamep = searchpath("vim_help.txt");
  72.                 if (fnamep == NULL ||
  73.                             (helpfd = fopen((char *)fnamep, READBIN)) == NULL)
  74.                 {
  75.                     smsg((char_u *)"Sorry, help file \"%s\" and \"vim_help.txt\" not found", p_hf);
  76.                     goto erret;
  77.                 }
  78. #else
  79.                 smsg((char_u *)"Sorry, help file \"%s\" not found", p_hf);
  80.                 goto erret;
  81. #endif
  82.             }
  83.             fclose(helpfd);
  84.  
  85.             if (win_split(0, FALSE) == FAIL)
  86.                 goto erret;
  87.             
  88.             if (curwin->w_height < p_hh)
  89.                 win_setheight((int)p_hh);
  90.  
  91. #ifdef RIGHTLEFT
  92.             curwin->w_p_rl = 0;                /* help window is left-to-right */
  93. #endif
  94.             curwin->w_p_nu = 0;                /* no line numbers */
  95.  
  96.             /*
  97.              * open help file (do_ecmd() will set b_help flag, readfile() will
  98.              * set b_p_ro flag)
  99.              */
  100.             (void)do_ecmd(0, fnamep, NULL, NULL, (linenr_t)0,
  101.                                                    ECMD_HIDE + ECMD_SET_HELP);
  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.     int        i;
  206.     static char *(mtable[]) = {"*", "g*", "[*", "]*",
  207.                                "/*", "/\\*", "/\\(\\)",
  208.                                "?", ":?", "?<CR>"};
  209.     static char *(rtable[]) = {"star", "gstar", "[star", "]star",
  210.                                "/star", "/\\\\star", "/\\\\(\\\\)",
  211.                                "?", ":?", "?<CR>"};
  212.  
  213.     reg_magic = p_magic;
  214.     d = IObuff;                /* assume IObuff is long enough! */
  215.  
  216.     /*
  217.      * Recognize a few exceptions to the rule.  Some strings that contain '*'
  218.      * with "star".  Otherwise '*' is recognized as a wildcard.
  219.      */
  220.     for (i = sizeof(mtable) / sizeof(char *); --i >= 0; )
  221.     {
  222.         if (STRCMP(arg, mtable[i]) == 0)
  223.         {
  224.             STRCPY(d, rtable[i]);
  225.             break;
  226.         }
  227.     }
  228.  
  229.     if (i < 0)        /* no match in table, replace single characters */
  230.     {
  231.         for (s = arg; *s; ++s)
  232.         {
  233.             /*
  234.              * Replace "|" with "bar" and """ with "quote" to match the name of
  235.              * the tags for these commands.
  236.              * Replace "*" with ".*" and "?" with "." to match command line
  237.              * completion.
  238.              * Insert a backslash before '~', '$' and '.' to avoid their
  239.              * special meaning.
  240.              */
  241.             if (d - IObuff > IOSIZE - 10)        /* getting too long!? */
  242.                 break;
  243.             switch (*s)
  244.             {
  245.                 case '|':    STRCPY(d, "bar");
  246.                             d += 3;
  247.                             continue;
  248.                 case '\"':    STRCPY(d, "quote");
  249.                             d += 5;
  250.                             continue;
  251.                 case '*':    *d++ = '.';
  252.                             break;
  253.                 case '?':    *d++ = '.';
  254.                             continue;
  255.                 case '$':
  256.                 case '.':
  257.                 case '~':    *d++ = '\\';
  258.                             break;
  259.             }
  260.  
  261.             /*
  262.              * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
  263.              * ":help i_^_CTRL-D" work.
  264.              */
  265.             if (*s < ' ' || (*s == '^' && s[1] && s[1] != '_'))    /* ^x */
  266.             {
  267.                 STRCPY(d, "CTRL-");
  268.                 d += 5;
  269.                 if (*s < ' ')
  270.                 {
  271.                     *d++ = *s + '@';
  272.                     continue;
  273.                 }
  274.                 ++s;
  275.             }
  276.             else if (*s == '^')            /* "^" or "CTRL-^" or "^_" */
  277.                 *d++ = '\\';
  278.  
  279.             /*
  280.              * Insert a backslash before a backslash after a slash, for search
  281.              * pattern tags: "/\|" --> "/\\|".
  282.              */
  283.             else if (s[0] == '\\' && s[1] != '\\' &&
  284.                                                   *arg == '/' && s == arg + 1)
  285.                 *d++ = '\\';
  286.  
  287.             *d++ = *s;
  288.  
  289.             /*
  290.              * If tag starts with ', toss everything after a second '. Fixes
  291.              * CTRL-] on 'option'. (would include the trailing '.').
  292.              */
  293.             if (*s == '\'' && s > arg && *arg == '\'')
  294.                 break;
  295.         }
  296.         *d = NUL;
  297.     }
  298.  
  299.     reg_ic = FALSE;
  300.     prog = vim_regcomp(IObuff);
  301.     if (prog == NULL)
  302.         return FAIL;
  303.  
  304.     /* First try to match with case, then without */
  305.     for (attempt = 0; attempt < 2; ++attempt, reg_ic = TRUE)
  306.     {
  307.         *matches = (char_u **)"";
  308.         *num_matches = 0;
  309.         retval = find_tags(NULL, prog, num_matches, matches, TRUE, FALSE);
  310.         if (retval == FAIL || *num_matches)
  311.             break;
  312.     }
  313.     vim_free(prog);
  314.  
  315. #ifdef HAVE_QSORT
  316.     /*
  317.      * Sort the matches found on the heuristic number that is after the
  318.      * tag name.  If there is no qsort, the output will be messy!
  319.      */
  320.     qsort((void *)*matches, (size_t)*num_matches,
  321.                                               sizeof(char_u *), help_compare);
  322. #endif
  323.     return OK;
  324. }
  325.