summaryrefslogtreecommitdiff
path: root/src/ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ops.c')
-rw-r--r--src/ops.c137
1 files changed, 133 insertions, 4 deletions
diff --git a/src/ops.c b/src/ops.c
index bc2860a0d..c1666ac69 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -112,6 +112,9 @@ static void may_set_selection __ARGS((void));
# endif
#endif
static void dis_msg __ARGS((char_u *p, int skip_esc));
+#if defined(FEAT_COMMENTS) || defined(PROTO)
+static char_u *skip_comment __ARGS((char_u *line, int process, int include_space, int *is_comment));
+#endif
#ifdef FEAT_VISUAL
static void block_prep __ARGS((oparg_T *oap, struct block_def *, linenr_T, int));
#endif
@@ -1987,7 +1990,7 @@ op_delete(oap)
curwin->w_cursor = curpos; /* restore curwin->w_cursor */
}
if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
- (void)do_join(2, FALSE, FALSE);
+ (void)do_join(2, FALSE, FALSE, FALSE);
}
}
@@ -4197,17 +4200,98 @@ dis_msg(p, skip_esc)
ui_breakcheck();
}
+#if defined(FEAT_COMMENTS) || defined(PROTO)
+/*
+ * If "process" is TRUE and the line begins with a comment leader (possibly
+ * after some white space), return a pointer to the text after it. Put a boolean
+ * value indicating whether the line ends with an unclosed comment in
+ * "is_comment".
+ * line - line to be processed,
+ * process - if FALSE, will only check whether the line ends with an unclosed
+ * comment,
+ * include_space - whether to also skip space following the comment leader,
+ * is_comment - will indicate whether the current line ends with an unclosed
+ * comment.
+ */
+ static char_u *
+skip_comment(line, process, include_space, is_comment)
+ char_u *line;
+ int process;
+ int include_space;
+ int *is_comment;
+{
+ char_u *comment_flags = NULL;
+ int lead_len;
+ int leader_offset = get_last_leader_offset(line, &comment_flags);
+
+ *is_comment = FALSE;
+ if (leader_offset != -1)
+ {
+ /* Let's check whether the line ends with an unclosed comment.
+ * If the last comment leader has COM_END in flags, there's no comment.
+ */
+ while (*comment_flags)
+ {
+ if (*comment_flags == COM_END
+ || *comment_flags == ':')
+ break;
+ ++comment_flags;
+ }
+ if (*comment_flags != COM_END)
+ *is_comment = TRUE;
+ }
+
+ if (process == FALSE)
+ return line;
+
+ lead_len = get_leader_len(line, &comment_flags, FALSE, include_space);
+
+ if (lead_len == 0)
+ return line;
+
+ /* Find:
+ * - COM_START,
+ * - COM_END,
+ * - colon,
+ * whichever comes first.
+ */
+ while (*comment_flags)
+ {
+ if (*comment_flags == COM_START
+ || *comment_flags == COM_END
+ || *comment_flags == ':')
+ {
+ break;
+ }
+ ++comment_flags;
+ }
+
+ /* If we found a colon, it means that we are not processing a line
+ * starting with an opening or a closing part of a three-part
+ * comment. That's good, because we don't want to remove those as
+ * this would be annoying.
+ */
+ if (*comment_flags == ':' || *comment_flags == NUL)
+ line += lead_len;
+
+ return line;
+}
+#endif
+
/*
* Join 'count' lines (minimal 2) at cursor position.
* When "save_undo" is TRUE save lines for undo first.
+ * Set "use_formatoptions" to FALSE when e.g. processing
+ * backspace and comment leaders should not be removed.
*
* return FAIL for failure, OK otherwise
*/
int
-do_join(count, insert_space, save_undo)
+do_join(count, insert_space, save_undo, use_formatoptions)
long count;
int insert_space;
int save_undo;
+ int use_formatoptions UNUSED;
{
char_u *curr = NULL;
char_u *curr_start = NULL;
@@ -4221,6 +4305,13 @@ do_join(count, insert_space, save_undo)
linenr_T t;
colnr_T col = 0;
int ret = OK;
+#if defined(FEAT_COMMENTS) || defined(PROTO)
+ int *comments;
+ int remove_comments = (use_formatoptions == TRUE)
+ && has_format_option(FO_REMOVE_COMS);
+ int prev_was_comment;
+#endif
+
if (save_undo && u_save((linenr_T)(curwin->w_cursor.lnum - 1),
(linenr_T)(curwin->w_cursor.lnum + count)) == FAIL)
@@ -4232,6 +4323,17 @@ do_join(count, insert_space, save_undo)
spaces = lalloc_clear((long_u)count, TRUE);
if (spaces == NULL)
return FAIL;
+#if defined(FEAT_COMMENTS) || defined(PROTO)
+ if (remove_comments)
+ {
+ comments = (int *)lalloc_clear((long_u)count * sizeof(int), TRUE);
+ if (comments == NULL)
+ {
+ vim_free(spaces);
+ return FAIL;
+ }
+ }
+#endif
/*
* Don't move anything, just compute the final line length
@@ -4240,6 +4342,25 @@ do_join(count, insert_space, save_undo)
for (t = 0; t < count; ++t)
{
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t));
+#if defined(FEAT_COMMENTS) || defined(PROTO)
+ if (remove_comments)
+ {
+ /* We don't want to remove the comment leader if the
+ * previous line is not a comment. */
+ if (t > 0 && prev_was_comment)
+ {
+
+ char_u *new_curr = skip_comment(curr, TRUE, insert_space,
+ &prev_was_comment);
+ comments[t] = new_curr - curr;
+ curr = new_curr;
+ }
+ else
+ curr = skip_comment(curr, FALSE, insert_space,
+ &prev_was_comment);
+ }
+#endif
+
if (insert_space && t > 0)
{
curr = skipwhite(curr);
@@ -4327,6 +4448,10 @@ do_join(count, insert_space, save_undo)
if (t == 0)
break;
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
+#if defined(FEAT_COMMENTS) || defined(PROTO)
+ if (remove_comments)
+ curr += comments[t - 1];
+#endif
if (insert_space && t > 1)
curr = skipwhite(curr);
currsize = (int)STRLEN(curr);
@@ -4364,6 +4489,10 @@ do_join(count, insert_space, save_undo)
theend:
vim_free(spaces);
+#if defined(FEAT_COMMENTS) || defined(PROTO)
+ if (remove_comments)
+ vim_free(comments);
+#endif
return ret;
}
@@ -4788,7 +4917,7 @@ format_lines(line_count, avoid_fex)
(long)-next_leader_len);
#endif
curwin->w_cursor.lnum--;
- if (do_join(2, TRUE, FALSE) == FAIL)
+ if (do_join(2, TRUE, FALSE, FALSE) == FAIL)
{
beep_flush();
break;
@@ -4844,7 +4973,7 @@ fmt_check_par(lnum, leader_len, leader_flags, do_comments)
ptr = ml_get(lnum);
if (do_comments)
- *leader_len = get_leader_len(ptr, leader_flags, FALSE);
+ *leader_len = get_leader_len(ptr, leader_flags, FALSE, TRUE);
else
*leader_len = 0;