comparison src/ex_cmds.c @ 15330:a6330a49e036 v8.1.0673

patch 8.1.0673: functionality for signs is spread out over several files commit https://github.com/vim/vim/commit/bbea47075cc4e7826e9f8c203e4272ba023ed7b0 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Jan 1 13:20:31 2019 +0100 patch 8.1.0673: functionality for signs is spread out over several files Problem: Functionality for signs is spread out over several files. Solution: Move most of the sign functionality into sign.c. (Yegappan Lakshmanan, closes #3751)
author Bram Moolenaar <Bram@vim.org>
date Tue, 01 Jan 2019 13:30:09 +0100
parents c6c306c5027f
children 273649cad196
comparison
equal deleted inserted replaced
15329:9a739a3d145e 15330:a6330a49e036
7577 do_helptags(dirname, add_help_tags); 7577 do_helptags(dirname, add_help_tags);
7578 vim_free(dirname); 7578 vim_free(dirname);
7579 } 7579 }
7580 } 7580 }
7581 7581
7582 #if defined(FEAT_SIGNS) || defined(PROTO)
7583
7584 /*
7585 * Struct to hold the sign properties.
7586 */
7587 typedef struct sign sign_T;
7588
7589 struct sign
7590 {
7591 sign_T *sn_next; /* next sign in list */
7592 int sn_typenr; /* type number of sign */
7593 char_u *sn_name; /* name of sign */
7594 char_u *sn_icon; /* name of pixmap */
7595 # ifdef FEAT_SIGN_ICONS
7596 void *sn_image; /* icon image */
7597 # endif
7598 char_u *sn_text; /* text used instead of pixmap */
7599 int sn_line_hl; /* highlight ID for line */
7600 int sn_text_hl; /* highlight ID for text */
7601 };
7602
7603 static sign_T *first_sign = NULL;
7604 static int next_sign_typenr = 1;
7605
7606 static void sign_list_defined(sign_T *sp);
7607 static void sign_undefine(sign_T *sp, sign_T *sp_prev);
7608
7609 static char *cmds[] = {
7610 "define",
7611 # define SIGNCMD_DEFINE 0
7612 "undefine",
7613 # define SIGNCMD_UNDEFINE 1
7614 "list",
7615 # define SIGNCMD_LIST 2
7616 "place",
7617 # define SIGNCMD_PLACE 3
7618 "unplace",
7619 # define SIGNCMD_UNPLACE 4
7620 "jump",
7621 # define SIGNCMD_JUMP 5
7622 NULL
7623 # define SIGNCMD_LAST 6
7624 };
7625
7626 /*
7627 * Find index of a ":sign" subcmd from its name.
7628 * "*end_cmd" must be writable.
7629 */
7630 static int
7631 sign_cmd_idx(
7632 char_u *begin_cmd, /* begin of sign subcmd */
7633 char_u *end_cmd) /* just after sign subcmd */
7634 {
7635 int idx;
7636 char save = *end_cmd;
7637
7638 *end_cmd = NUL;
7639 for (idx = 0; ; ++idx)
7640 if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0)
7641 break;
7642 *end_cmd = save;
7643 return idx;
7644 }
7645
7646 /*
7647 * Find a sign by name. Also returns pointer to the previous sign.
7648 */
7649 static sign_T *
7650 sign_find(char_u *name, sign_T **sp_prev)
7651 {
7652 sign_T *sp;
7653
7654 if (sp_prev != NULL)
7655 *sp_prev = NULL;
7656 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
7657 {
7658 if (STRCMP(sp->sn_name, name) == 0)
7659 break;
7660 if (sp_prev != NULL)
7661 *sp_prev = sp;
7662 }
7663
7664 return sp;
7665 }
7666
7667 /*
7668 * Define a new sign or update an existing sign
7669 */
7670 int
7671 sign_define_by_name(
7672 char_u *name,
7673 char_u *icon,
7674 char_u *linehl,
7675 char_u *text,
7676 char_u *texthl)
7677 {
7678 sign_T *sp_prev;
7679 sign_T *sp;
7680
7681 sp = sign_find(name, &sp_prev);
7682 if (sp == NULL)
7683 {
7684 sign_T *lp;
7685 int start = next_sign_typenr;
7686
7687 // Allocate a new sign.
7688 sp = (sign_T *)alloc_clear_id((unsigned)sizeof(sign_T),
7689 aid_sign_define_by_name);
7690 if (sp == NULL)
7691 return FAIL;
7692
7693 // Check that next_sign_typenr is not already being used.
7694 // This only happens after wrapping around. Hopefully
7695 // another one got deleted and we can use its number.
7696 for (lp = first_sign; lp != NULL; )
7697 {
7698 if (lp->sn_typenr == next_sign_typenr)
7699 {
7700 ++next_sign_typenr;
7701 if (next_sign_typenr == MAX_TYPENR)
7702 next_sign_typenr = 1;
7703 if (next_sign_typenr == start)
7704 {
7705 vim_free(sp);
7706 EMSG(_("E612: Too many signs defined"));
7707 return FAIL;
7708 }
7709 lp = first_sign; // start all over
7710 continue;
7711 }
7712 lp = lp->sn_next;
7713 }
7714
7715 sp->sn_typenr = next_sign_typenr;
7716 if (++next_sign_typenr == MAX_TYPENR)
7717 next_sign_typenr = 1; // wrap around
7718
7719 sp->sn_name = vim_strsave(name);
7720 if (sp->sn_name == NULL) // out of memory
7721 {
7722 vim_free(sp);
7723 return FAIL;
7724 }
7725
7726 // add the new sign to the list of signs
7727 if (sp_prev == NULL)
7728 first_sign = sp;
7729 else
7730 sp_prev->sn_next = sp;
7731 }
7732
7733 // set values for a defined sign.
7734 if (icon != NULL)
7735 {
7736 vim_free(sp->sn_icon);
7737 sp->sn_icon = vim_strsave(icon);
7738 backslash_halve(sp->sn_icon);
7739 # ifdef FEAT_SIGN_ICONS
7740 if (gui.in_use)
7741 {
7742 out_flush();
7743 if (sp->sn_image != NULL)
7744 gui_mch_destroy_sign(sp->sn_image);
7745 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
7746 }
7747 # endif
7748 }
7749
7750 if (text != NULL)
7751 {
7752 char_u *s;
7753 char_u *endp;
7754 int cells;
7755 int len;
7756
7757 endp = text + (int)STRLEN(text);
7758 for (s = text; s + 1 < endp; ++s)
7759 if (*s == '\\')
7760 {
7761 // Remove a backslash, so that it is possible
7762 // to use a space.
7763 STRMOVE(s, s + 1);
7764 --endp;
7765 }
7766 # ifdef FEAT_MBYTE
7767 // Count cells and check for non-printable chars
7768 if (has_mbyte)
7769 {
7770 cells = 0;
7771 for (s = text; s < endp; s += (*mb_ptr2len)(s))
7772 {
7773 if (!vim_isprintc((*mb_ptr2char)(s)))
7774 break;
7775 cells += (*mb_ptr2cells)(s);
7776 }
7777 }
7778 else
7779 # endif
7780 {
7781 for (s = text; s < endp; ++s)
7782 if (!vim_isprintc(*s))
7783 break;
7784 cells = (int)(s - text);
7785 }
7786 // Currently must be one or two display cells
7787 if (s != endp || cells < 1 || cells > 2)
7788 {
7789 EMSG2(_("E239: Invalid sign text: %s"), text);
7790 return FAIL;
7791 }
7792
7793 vim_free(sp->sn_text);
7794 // Allocate one byte more if we need to pad up
7795 // with a space.
7796 len = (int)(endp - text + ((cells == 1) ? 1 : 0));
7797 sp->sn_text = vim_strnsave(text, len);
7798
7799 if (sp->sn_text != NULL && cells == 1)
7800 STRCPY(sp->sn_text + len - 1, " ");
7801 }
7802
7803 if (linehl != NULL)
7804 sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl));
7805
7806 if (texthl != NULL)
7807 sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl));
7808
7809 return OK;
7810 }
7811
7812 /*
7813 * Free the sign specified by 'name'.
7814 */
7815 int
7816 sign_undefine_by_name(char_u *name)
7817 {
7818 sign_T *sp_prev;
7819 sign_T *sp;
7820
7821 sp = sign_find(name, &sp_prev);
7822 if (sp == NULL)
7823 {
7824 EMSG2(_("E155: Unknown sign: %s"), name);
7825 return FAIL;
7826 }
7827 sign_undefine(sp, sp_prev);
7828
7829 return OK;
7830 }
7831
7832 /*
7833 * List the signs matching 'name'
7834 */
7835 static void
7836 sign_list_by_name(char_u *name)
7837 {
7838 sign_T *sp;
7839
7840 sp = sign_find(name, NULL);
7841 if (sp != NULL)
7842 sign_list_defined(sp);
7843 else
7844 EMSG2(_("E155: Unknown sign: %s"), name);
7845 }
7846
7847 /*
7848 * Place a sign at the specifed file location or update a sign.
7849 */
7850 int
7851 sign_place(
7852 int *sign_id,
7853 char_u *sign_group,
7854 char_u *sign_name,
7855 buf_T *buf,
7856 linenr_T lnum,
7857 int prio)
7858 {
7859 sign_T *sp;
7860
7861 // Check for reserved character '*' in group name
7862 if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0'))
7863 return FAIL;
7864
7865 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
7866 if (STRCMP(sp->sn_name, sign_name) == 0)
7867 break;
7868 if (sp == NULL)
7869 {
7870 EMSG2(_("E155: Unknown sign: %s"), sign_name);
7871 return FAIL;
7872 }
7873 if (*sign_id == 0)
7874 *sign_id = sign_group_get_next_signid(buf, sign_group);
7875
7876 if (lnum > 0)
7877 // ":sign place {id} line={lnum} name={name} file={fname}":
7878 // place a sign
7879 buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr);
7880 else
7881 // ":sign place {id} file={fname}": change sign type
7882 lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr);
7883 if (lnum > 0)
7884 update_debug_sign(buf, lnum);
7885 else
7886 {
7887 EMSG2(_("E885: Not possible to change sign %s"), sign_name);
7888 return FAIL;
7889 }
7890
7891 return OK;
7892 }
7893
7894 /*
7895 * Unplace the specified sign
7896 */
7897 int
7898 sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum)
7899 {
7900 if (buf->b_signlist == NULL) // No signs in the buffer
7901 return OK;
7902
7903 if (sign_id == 0)
7904 {
7905 // Delete all the signs in the specified buffer
7906 redraw_buf_later(buf, NOT_VALID);
7907 buf_delete_signs(buf, sign_group);
7908 }
7909 else
7910 {
7911 linenr_T lnum;
7912
7913 // Delete only the specified signs
7914 lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
7915 if (lnum == 0)
7916 return FAIL;
7917 }
7918
7919 return OK;
7920 }
7921
7922 /*
7923 * Unplace the sign at the current cursor line.
7924 */
7925 static void
7926 sign_unplace_at_cursor(char_u *groupname)
7927 {
7928 int id = -1;
7929
7930 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
7931 if (id > 0)
7932 sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
7933 else
7934 EMSG(_("E159: Missing sign number"));
7935 }
7936
7937 /*
7938 * sign define command
7939 * ":sign define {name} ..."
7940 */
7941 static void
7942 sign_define_cmd(char_u *sign_name, char_u *cmdline)
7943 {
7944 char_u *arg;
7945 char_u *p = cmdline;
7946 char_u *icon = NULL;
7947 char_u *text = NULL;
7948 char_u *linehl = NULL;
7949 char_u *texthl = NULL;
7950 int failed = FALSE;
7951
7952 // set values for a defined sign.
7953 for (;;)
7954 {
7955 arg = skipwhite(p);
7956 if (*arg == NUL)
7957 break;
7958 p = skiptowhite_esc(arg);
7959 if (STRNCMP(arg, "icon=", 5) == 0)
7960 {
7961 arg += 5;
7962 icon = vim_strnsave(arg, (int)(p - arg));
7963 }
7964 else if (STRNCMP(arg, "text=", 5) == 0)
7965 {
7966 arg += 5;
7967 text = vim_strnsave(arg, (int)(p - arg));
7968 }
7969 else if (STRNCMP(arg, "linehl=", 7) == 0)
7970 {
7971 arg += 7;
7972 linehl = vim_strnsave(arg, (int)(p - arg));
7973 }
7974 else if (STRNCMP(arg, "texthl=", 7) == 0)
7975 {
7976 arg += 7;
7977 texthl = vim_strnsave(arg, (int)(p - arg));
7978 }
7979 else
7980 {
7981 EMSG2(_(e_invarg2), arg);
7982 failed = TRUE;
7983 break;
7984 }
7985 }
7986
7987 if (!failed)
7988 sign_define_by_name(sign_name, icon, linehl, text, texthl);
7989
7990 vim_free(icon);
7991 vim_free(text);
7992 vim_free(linehl);
7993 vim_free(texthl);
7994 }
7995
7996 /*
7997 * :sign place command
7998 */
7999 static void
8000 sign_place_cmd(
8001 buf_T *buf,
8002 linenr_T lnum,
8003 char_u *sign_name,
8004 int id,
8005 char_u *group,
8006 int prio)
8007 {
8008 if (id <= 0)
8009 {
8010 // List signs placed in a file/buffer
8011 // :sign place file={fname}
8012 // :sign place group={group} file={fname}
8013 // :sign place group=* file={fname}
8014 // :sign place buffer={nr}
8015 // :sign place group={group} buffer={nr}
8016 // :sign place group=* buffer={nr}
8017 // :sign place
8018 // :sign place group={group}
8019 // :sign place group=*
8020 if (lnum >= 0 || sign_name != NULL ||
8021 (group != NULL && *group == '\0'))
8022 EMSG(_(e_invarg));
8023 else
8024 sign_list_placed(buf, group);
8025 }
8026 else
8027 {
8028 // Place a new sign
8029 if (sign_name == NULL || buf == NULL ||
8030 (group != NULL && *group == '\0'))
8031 {
8032 EMSG(_(e_invarg));
8033 return;
8034 }
8035
8036 sign_place(&id, group, sign_name, buf, lnum, prio);
8037 }
8038 }
8039
8040 /*
8041 * :sign unplace command
8042 */
8043 static void
8044 sign_unplace_cmd(
8045 buf_T *buf,
8046 linenr_T lnum,
8047 char_u *sign_name,
8048 int id,
8049 char_u *group)
8050 {
8051 if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0'))
8052 {
8053 EMSG(_(e_invarg));
8054 return;
8055 }
8056
8057 if (id == -2)
8058 {
8059 if (buf != NULL)
8060 // :sign unplace * file={fname}
8061 // :sign unplace * group={group} file={fname}
8062 // :sign unplace * group=* file={fname}
8063 // :sign unplace * buffer={nr}
8064 // :sign unplace * group={group} buffer={nr}
8065 // :sign unplace * group=* buffer={nr}
8066 sign_unplace(0, group, buf, 0);
8067 else
8068 // :sign unplace *
8069 // :sign unplace * group={group}
8070 // :sign unplace * group=*
8071 FOR_ALL_BUFFERS(buf)
8072 if (buf->b_signlist != NULL)
8073 buf_delete_signs(buf, group);
8074 }
8075 else
8076 {
8077 if (buf != NULL)
8078 // :sign unplace {id} file={fname}
8079 // :sign unplace {id} group={group} file={fname}
8080 // :sign unplace {id} group=* file={fname}
8081 // :sign unplace {id} buffer={nr}
8082 // :sign unplace {id} group={group} buffer={nr}
8083 // :sign unplace {id} group=* buffer={nr}
8084 sign_unplace(id, group, buf, 0);
8085 else
8086 {
8087 if (id == -1)
8088 {
8089 // :sign unplace group={group}
8090 // :sign unplace group=*
8091 sign_unplace_at_cursor(group);
8092 }
8093 else
8094 {
8095 // :sign unplace {id}
8096 // :sign unplace {id} group={group}
8097 // :sign unplace {id} group=*
8098 FOR_ALL_BUFFERS(buf)
8099 sign_unplace(id, group, buf, 0);
8100 }
8101 }
8102 }
8103 }
8104
8105 /*
8106 * Jump to a placed sign
8107 * :sign jump {id} file={fname}
8108 * :sign jump {id} buffer={nr}
8109 * :sign jump {id} group={group} file={fname}
8110 * :sign jump {id} group={group} buffer={nr}
8111 */
8112 static void
8113 sign_jump_cmd(
8114 buf_T *buf,
8115 linenr_T lnum,
8116 char_u *sign_name,
8117 int id,
8118 char_u *group)
8119 {
8120 if (buf == NULL && sign_name == NULL && group == NULL && id == -1)
8121 {
8122 EMSG(_(e_argreq));
8123 return;
8124 }
8125
8126 if (buf == NULL || (group != NULL && *group == '\0') ||
8127 lnum >= 0 || sign_name != NULL)
8128 {
8129 // File or buffer is not specified or an empty group is used
8130 // or a line number or a sign name is specified.
8131 EMSG(_(e_invarg));
8132 return;
8133 }
8134
8135 if ((lnum = buf_findsign(buf, id, group)) <= 0)
8136 {
8137 EMSGN(_("E157: Invalid sign ID: %ld"), id);
8138 return;
8139 }
8140
8141 // goto a sign ...
8142 if (buf_jump_open_win(buf) != NULL)
8143 { // ... in a current window
8144 curwin->w_cursor.lnum = lnum;
8145 check_cursor_lnum();
8146 beginline(BL_WHITE);
8147 }
8148 else
8149 { // ... not currently in a window
8150 char_u *cmd;
8151
8152 if (buf->b_fname == NULL)
8153 {
8154 EMSG(_("E934: Cannot jump to a buffer that does not have a name"));
8155 return;
8156 }
8157 cmd = alloc((unsigned)STRLEN(buf->b_fname) + 25);
8158 if (cmd == NULL)
8159 return;
8160 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
8161 do_cmdline_cmd(cmd);
8162 vim_free(cmd);
8163 }
8164 # ifdef FEAT_FOLDING
8165 foldOpenCursor();
8166 # endif
8167 }
8168
8169 /*
8170 * Parse the command line arguments for the ":sign place", ":sign unplace" and
8171 * ":sign jump" commands.
8172 * The supported arguments are: line={lnum} name={name} group={group}
8173 * priority={prio} and file={fname} or buffer={nr}.
8174 */
8175 static int
8176 parse_sign_cmd_args(
8177 int cmd,
8178 char_u *arg,
8179 char_u **sign_name,
8180 int *signid,
8181 char_u **group,
8182 int *prio,
8183 buf_T **buf,
8184 linenr_T *lnum)
8185 {
8186 char_u *arg1;
8187 char_u *name;
8188 char_u *filename = NULL;
8189
8190 // first arg could be placed sign id
8191 arg1 = arg;
8192 if (VIM_ISDIGIT(*arg))
8193 {
8194 *signid = getdigits(&arg);
8195 if (!VIM_ISWHITE(*arg) && *arg != NUL)
8196 {
8197 *signid = -1;
8198 arg = arg1;
8199 }
8200 else
8201 arg = skipwhite(arg);
8202 }
8203
8204 while (*arg != NUL)
8205 {
8206 if (STRNCMP(arg, "line=", 5) == 0)
8207 {
8208 arg += 5;
8209 *lnum = atoi((char *)arg);
8210 arg = skiptowhite(arg);
8211 }
8212 else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE)
8213 {
8214 if (*signid != -1)
8215 {
8216 EMSG(_(e_invarg));
8217 return FAIL;
8218 }
8219 *signid = -2;
8220 arg = skiptowhite(arg + 1);
8221 }
8222 else if (STRNCMP(arg, "name=", 5) == 0)
8223 {
8224 arg += 5;
8225 name = arg;
8226 arg = skiptowhite(arg);
8227 if (*arg != NUL)
8228 *arg++ = NUL;
8229 while (name[0] == '0' && name[1] != NUL)
8230 ++name;
8231 *sign_name = name;
8232 }
8233 else if (STRNCMP(arg, "group=", 6) == 0)
8234 {
8235 arg += 6;
8236 *group = arg;
8237 arg = skiptowhite(arg);
8238 if (*arg != NUL)
8239 *arg++ = NUL;
8240 }
8241 else if (STRNCMP(arg, "priority=", 9) == 0)
8242 {
8243 arg += 9;
8244 *prio = atoi((char *)arg);
8245 arg = skiptowhite(arg);
8246 }
8247 else if (STRNCMP(arg, "file=", 5) == 0)
8248 {
8249 arg += 5;
8250 filename = arg;
8251 *buf = buflist_findname_exp(arg);
8252 break;
8253 }
8254 else if (STRNCMP(arg, "buffer=", 7) == 0)
8255 {
8256 arg += 7;
8257 filename = arg;
8258 *buf = buflist_findnr((int)getdigits(&arg));
8259 if (*skipwhite(arg) != NUL)
8260 EMSG(_(e_trailing));
8261 break;
8262 }
8263 else
8264 {
8265 EMSG(_(e_invarg));
8266 return FAIL;
8267 }
8268 arg = skipwhite(arg);
8269 }
8270
8271 if (filename != NULL && *buf == NULL)
8272 {
8273 EMSG2(_("E158: Invalid buffer name: %s"), filename);
8274 return FAIL;
8275 }
8276
8277 return OK;
8278 }
8279
8280 /*
8281 * ":sign" command
8282 */
8283 void
8284 ex_sign(exarg_T *eap)
8285 {
8286 char_u *arg = eap->arg;
8287 char_u *p;
8288 int idx;
8289 sign_T *sp;
8290 buf_T *buf = NULL;
8291
8292 // Parse the subcommand.
8293 p = skiptowhite(arg);
8294 idx = sign_cmd_idx(arg, p);
8295 if (idx == SIGNCMD_LAST)
8296 {
8297 EMSG2(_("E160: Unknown sign command: %s"), arg);
8298 return;
8299 }
8300 arg = skipwhite(p);
8301
8302 if (idx <= SIGNCMD_LIST)
8303 {
8304 // Define, undefine or list signs.
8305 if (idx == SIGNCMD_LIST && *arg == NUL)
8306 {
8307 // ":sign list": list all defined signs
8308 for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next)
8309 sign_list_defined(sp);
8310 }
8311 else if (*arg == NUL)
8312 EMSG(_("E156: Missing sign name"));
8313 else
8314 {
8315 char_u *name;
8316
8317 // Isolate the sign name. If it's a number skip leading zeroes,
8318 // so that "099" and "99" are the same sign. But keep "0".
8319 p = skiptowhite(arg);
8320 if (*p != NUL)
8321 *p++ = NUL;
8322 while (arg[0] == '0' && arg[1] != NUL)
8323 ++arg;
8324 name = vim_strsave(arg);
8325
8326 if (idx == SIGNCMD_DEFINE)
8327 sign_define_cmd(name, p);
8328 else if (idx == SIGNCMD_LIST)
8329 // ":sign list {name}"
8330 sign_list_by_name(name);
8331 else
8332 // ":sign undefine {name}"
8333 sign_undefine_by_name(name);
8334
8335 vim_free(name);
8336 return;
8337 }
8338 }
8339 else
8340 {
8341 int id = -1;
8342 linenr_T lnum = -1;
8343 char_u *sign_name = NULL;
8344 char_u *group = NULL;
8345 int prio = SIGN_DEF_PRIO;
8346
8347 // Parse command line arguments
8348 if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
8349 &buf, &lnum) == FAIL)
8350 return;
8351
8352 if (idx == SIGNCMD_PLACE)
8353 sign_place_cmd(buf, lnum, sign_name, id, group, prio);
8354 else if (idx == SIGNCMD_UNPLACE)
8355 sign_unplace_cmd(buf, lnum, sign_name, id, group);
8356 else if (idx == SIGNCMD_JUMP)
8357 sign_jump_cmd(buf, lnum, sign_name, id, group);
8358 }
8359 }
8360
8361 /*
8362 * Return information about a specified sign
8363 */
8364 static void
8365 sign_getinfo(sign_T *sp, dict_T *retdict)
8366 {
8367 char_u *p;
8368
8369 dict_add_string(retdict, "name", (char_u *)sp->sn_name);
8370 if (sp->sn_icon != NULL)
8371 dict_add_string(retdict, "icon", (char_u *)sp->sn_icon);
8372 if (sp->sn_text != NULL)
8373 dict_add_string(retdict, "text", (char_u *)sp->sn_text);
8374 if (sp->sn_line_hl > 0)
8375 {
8376 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
8377 if (p == NULL)
8378 p = (char_u *)"NONE";
8379 dict_add_string(retdict, "linehl", (char_u *)p);
8380 }
8381 if (sp->sn_text_hl > 0)
8382 {
8383 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
8384 if (p == NULL)
8385 p = (char_u *)"NONE";
8386 dict_add_string(retdict, "texthl", (char_u *)p);
8387 }
8388 }
8389
8390 /*
8391 * If 'name' is NULL, return a list of all the defined signs.
8392 * Otherwise, return information about the specified sign.
8393 */
8394 void
8395 sign_getlist(char_u *name, list_T *retlist)
8396 {
8397 sign_T *sp = first_sign;
8398 dict_T *dict;
8399
8400 if (name != NULL)
8401 {
8402 sp = sign_find(name, NULL);
8403 if (sp == NULL)
8404 return;
8405 }
8406
8407 for (; sp != NULL && !got_int; sp = sp->sn_next)
8408 {
8409 if ((dict = dict_alloc_id(aid_sign_getlist)) == NULL)
8410 return;
8411 if (list_append_dict(retlist, dict) == FAIL)
8412 return;
8413 sign_getinfo(sp, dict);
8414
8415 if (name != NULL) // handle only the specified sign
8416 break;
8417 }
8418 }
8419
8420 /*
8421 * Return information about all the signs placed in a buffer
8422 */
8423 static void
8424 sign_get_placed_in_buf(
8425 buf_T *buf,
8426 linenr_T lnum,
8427 int sign_id,
8428 char_u *sign_group,
8429 list_T *retlist)
8430 {
8431 dict_T *d;
8432 list_T *l;
8433 signlist_T *sign;
8434 dict_T *sdict;
8435
8436 if ((d = dict_alloc_id(aid_sign_getplaced_dict)) == NULL)
8437 return;
8438 list_append_dict(retlist, d);
8439
8440 dict_add_number(d, "bufnr", (long)buf->b_fnum);
8441
8442 if ((l = list_alloc_id(aid_sign_getplaced_list)) == NULL)
8443 return;
8444 dict_add_list(d, "signs", l);
8445
8446 FOR_ALL_SIGNS_IN_BUF(buf, sign)
8447 {
8448 if (!sign_in_group(sign, sign_group))
8449 continue;
8450 if ((lnum == 0 && sign_id == 0) ||
8451 (sign_id == 0 && lnum == sign->lnum) ||
8452 (lnum == 0 && sign_id == sign->id) ||
8453 (lnum == sign->lnum && sign_id == sign->id))
8454 {
8455 if ((sdict = sign_get_info(sign)) != NULL)
8456 list_append_dict(l, sdict);
8457 }
8458 }
8459 }
8460
8461 /*
8462 * Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
8463 * sign placed at the line number. If 'lnum' is zero, return all the signs
8464 * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
8465 */
8466 void
8467 sign_get_placed(
8468 buf_T *buf,
8469 linenr_T lnum,
8470 int sign_id,
8471 char_u *sign_group,
8472 list_T *retlist)
8473 {
8474 if (buf != NULL)
8475 sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
8476 else
8477 {
8478 FOR_ALL_BUFFERS(buf)
8479 {
8480 if (buf->b_signlist != NULL)
8481 sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist);
8482 }
8483 }
8484 }
8485
8486 # if defined(FEAT_SIGN_ICONS) || defined(PROTO)
8487 /*
8488 * Allocate the icons. Called when the GUI has started. Allows defining
8489 * signs before it starts.
8490 */
8491 void
8492 sign_gui_started(void)
8493 {
8494 sign_T *sp;
8495
8496 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
8497 if (sp->sn_icon != NULL)
8498 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
8499 }
8500 # endif
8501
8502 /*
8503 * List one sign.
8504 */
8505 static void
8506 sign_list_defined(sign_T *sp)
8507 {
8508 char_u *p;
8509
8510 smsg((char_u *)"sign %s", sp->sn_name);
8511 if (sp->sn_icon != NULL)
8512 {
8513 MSG_PUTS(" icon=");
8514 msg_outtrans(sp->sn_icon);
8515 # ifdef FEAT_SIGN_ICONS
8516 if (sp->sn_image == NULL)
8517 MSG_PUTS(_(" (NOT FOUND)"));
8518 # else
8519 MSG_PUTS(_(" (not supported)"));
8520 # endif
8521 }
8522 if (sp->sn_text != NULL)
8523 {
8524 MSG_PUTS(" text=");
8525 msg_outtrans(sp->sn_text);
8526 }
8527 if (sp->sn_line_hl > 0)
8528 {
8529 MSG_PUTS(" linehl=");
8530 p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE);
8531 if (p == NULL)
8532 MSG_PUTS("NONE");
8533 else
8534 msg_puts(p);
8535 }
8536 if (sp->sn_text_hl > 0)
8537 {
8538 MSG_PUTS(" texthl=");
8539 p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE);
8540 if (p == NULL)
8541 MSG_PUTS("NONE");
8542 else
8543 msg_puts(p);
8544 }
8545 }
8546
8547 /*
8548 * Undefine a sign and free its memory.
8549 */
8550 static void
8551 sign_undefine(sign_T *sp, sign_T *sp_prev)
8552 {
8553 vim_free(sp->sn_name);
8554 vim_free(sp->sn_icon);
8555 # ifdef FEAT_SIGN_ICONS
8556 if (sp->sn_image != NULL)
8557 {
8558 out_flush();
8559 gui_mch_destroy_sign(sp->sn_image);
8560 }
8561 # endif
8562 vim_free(sp->sn_text);
8563 if (sp_prev == NULL)
8564 first_sign = sp->sn_next;
8565 else
8566 sp_prev->sn_next = sp->sn_next;
8567 vim_free(sp);
8568 }
8569
8570 /*
8571 * Get highlighting attribute for sign "typenr".
8572 * If "line" is TRUE: line highl, if FALSE: text highl.
8573 */
8574 int
8575 sign_get_attr(int typenr, int line)
8576 {
8577 sign_T *sp;
8578
8579 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
8580 if (sp->sn_typenr == typenr)
8581 {
8582 if (line)
8583 {
8584 if (sp->sn_line_hl > 0)
8585 return syn_id2attr(sp->sn_line_hl);
8586 }
8587 else
8588 {
8589 if (sp->sn_text_hl > 0)
8590 return syn_id2attr(sp->sn_text_hl);
8591 }
8592 break;
8593 }
8594 return 0;
8595 }
8596
8597 /*
8598 * Get text mark for sign "typenr".
8599 * Returns NULL if there isn't one.
8600 */
8601 char_u *
8602 sign_get_text(int typenr)
8603 {
8604 sign_T *sp;
8605
8606 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
8607 if (sp->sn_typenr == typenr)
8608 return sp->sn_text;
8609 return NULL;
8610 }
8611
8612 # if defined(FEAT_SIGN_ICONS) || defined(PROTO)
8613 void *
8614 sign_get_image(
8615 int typenr) /* the attribute which may have a sign */
8616 {
8617 sign_T *sp;
8618
8619 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
8620 if (sp->sn_typenr == typenr)
8621 return sp->sn_image;
8622 return NULL;
8623 }
8624 # endif
8625
8626 /*
8627 * Get the name of a sign by its typenr.
8628 */
8629 char_u *
8630 sign_typenr2name(int typenr)
8631 {
8632 sign_T *sp;
8633
8634 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
8635 if (sp->sn_typenr == typenr)
8636 return sp->sn_name;
8637 return (char_u *)_("[Deleted]");
8638 }
8639
8640 /*
8641 * Undefine/free all signs.
8642 */
8643 void
8644 free_signs(void)
8645 {
8646 while (first_sign != NULL)
8647 sign_undefine(first_sign, NULL);
8648 }
8649
8650 # if defined(FEAT_CMDL_COMPL) || defined(PROTO)
8651 static enum
8652 {
8653 EXP_SUBCMD, /* expand :sign sub-commands */
8654 EXP_DEFINE, /* expand :sign define {name} args */
8655 EXP_PLACE, /* expand :sign place {id} args */
8656 EXP_UNPLACE, /* expand :sign unplace" */
8657 EXP_SIGN_NAMES /* expand with name of placed signs */
8658 } expand_what;
8659
8660 /*
8661 * Function given to ExpandGeneric() to obtain the sign command
8662 * expansion.
8663 */
8664 char_u *
8665 get_sign_name(expand_T *xp UNUSED, int idx)
8666 {
8667 sign_T *sp;
8668 int current_idx;
8669
8670 switch (expand_what)
8671 {
8672 case EXP_SUBCMD:
8673 return (char_u *)cmds[idx];
8674 case EXP_DEFINE:
8675 {
8676 char *define_arg[] =
8677 {
8678 "icon=", "linehl=", "text=", "texthl=", NULL
8679 };
8680 return (char_u *)define_arg[idx];
8681 }
8682 case EXP_PLACE:
8683 {
8684 char *place_arg[] =
8685 {
8686 "line=", "name=", "group=", "priority=", "file=",
8687 "buffer=", NULL
8688 };
8689 return (char_u *)place_arg[idx];
8690 }
8691 case EXP_UNPLACE:
8692 {
8693 char *unplace_arg[] = { "group=", "file=", "buffer=", NULL };
8694 return (char_u *)unplace_arg[idx];
8695 }
8696 case EXP_SIGN_NAMES:
8697 /* Complete with name of signs already defined */
8698 current_idx = 0;
8699 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
8700 if (current_idx++ == idx)
8701 return sp->sn_name;
8702 return NULL;
8703 default:
8704 return NULL;
8705 }
8706 }
8707
8708 /*
8709 * Handle command line completion for :sign command.
8710 */
8711 void
8712 set_context_in_sign_cmd(expand_T *xp, char_u *arg)
8713 {
8714 char_u *p;
8715 char_u *end_subcmd;
8716 char_u *last;
8717 int cmd_idx;
8718 char_u *begin_subcmd_args;
8719
8720 /* Default: expand subcommands. */
8721 xp->xp_context = EXPAND_SIGN;
8722 expand_what = EXP_SUBCMD;
8723 xp->xp_pattern = arg;
8724
8725 end_subcmd = skiptowhite(arg);
8726 if (*end_subcmd == NUL)
8727 /* expand subcmd name
8728 * :sign {subcmd}<CTRL-D>*/
8729 return;
8730
8731 cmd_idx = sign_cmd_idx(arg, end_subcmd);
8732
8733 /* :sign {subcmd} {subcmd_args}
8734 * |
8735 * begin_subcmd_args */
8736 begin_subcmd_args = skipwhite(end_subcmd);
8737 p = skiptowhite(begin_subcmd_args);
8738 if (*p == NUL)
8739 {
8740 /*
8741 * Expand first argument of subcmd when possible.
8742 * For ":jump {id}" and ":unplace {id}", we could
8743 * possibly expand the ids of all signs already placed.
8744 */
8745 xp->xp_pattern = begin_subcmd_args;
8746 switch (cmd_idx)
8747 {
8748 case SIGNCMD_LIST:
8749 case SIGNCMD_UNDEFINE:
8750 /* :sign list <CTRL-D>
8751 * :sign undefine <CTRL-D> */
8752 expand_what = EXP_SIGN_NAMES;
8753 break;
8754 default:
8755 xp->xp_context = EXPAND_NOTHING;
8756 }
8757 return;
8758 }
8759
8760 /* expand last argument of subcmd */
8761
8762 /* :sign define {name} {args}...
8763 * |
8764 * p */
8765
8766 /* Loop until reaching last argument. */
8767 do
8768 {
8769 p = skipwhite(p);
8770 last = p;
8771 p = skiptowhite(p);
8772 } while (*p != NUL);
8773
8774 p = vim_strchr(last, '=');
8775
8776 /* :sign define {name} {args}... {last}=
8777 * | |
8778 * last p */
8779 if (p == NULL)
8780 {
8781 /* Expand last argument name (before equal sign). */
8782 xp->xp_pattern = last;
8783 switch (cmd_idx)
8784 {
8785 case SIGNCMD_DEFINE:
8786 expand_what = EXP_DEFINE;
8787 break;
8788 case SIGNCMD_PLACE:
8789 expand_what = EXP_PLACE;
8790 break;
8791 case SIGNCMD_JUMP:
8792 case SIGNCMD_UNPLACE:
8793 expand_what = EXP_UNPLACE;
8794 break;
8795 default:
8796 xp->xp_context = EXPAND_NOTHING;
8797 }
8798 }
8799 else
8800 {
8801 /* Expand last argument value (after equal sign). */
8802 xp->xp_pattern = p + 1;
8803 switch (cmd_idx)
8804 {
8805 case SIGNCMD_DEFINE:
8806 if (STRNCMP(last, "texthl", p - last) == 0 ||
8807 STRNCMP(last, "linehl", p - last) == 0)
8808 xp->xp_context = EXPAND_HIGHLIGHT;
8809 else if (STRNCMP(last, "icon", p - last) == 0)
8810 xp->xp_context = EXPAND_FILES;
8811 else
8812 xp->xp_context = EXPAND_NOTHING;
8813 break;
8814 case SIGNCMD_PLACE:
8815 if (STRNCMP(last, "name", p - last) == 0)
8816 expand_what = EXP_SIGN_NAMES;
8817 else
8818 xp->xp_context = EXPAND_NOTHING;
8819 break;
8820 default:
8821 xp->xp_context = EXPAND_NOTHING;
8822 }
8823 }
8824 }
8825 # endif
8826 #endif
8827
8828 /* 7582 /*
8829 * Make the user happy. 7583 * Make the user happy.
8830 */ 7584 */
8831 void 7585 void
8832 ex_smile(exarg_T *eap UNUSED) 7586 ex_smile(exarg_T *eap UNUSED)