Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
278 changes: 189 additions & 89 deletions src/editor/edit.c

Large diffs are not rendered by default.

140 changes: 140 additions & 0 deletions src/editor/editbuffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ edit_buffer_init (edit_buffer_t *buf, off_t size)

buf->size = size;
buf->lines = 0;

buf->lb_detected = LB_ASIS;
buf->lb_dirty = TRUE;
}

/* --------------------------------------------------------------------------------------------- */
Expand Down Expand Up @@ -506,6 +509,9 @@ edit_buffer_insert (edit_buffer_t *buf, int c)

// update file length
buf->size++;

if (c == '\r' || c == '\n')
buf->lb_dirty = TRUE;
}

/* --------------------------------------------------------------------------------------------- */
Expand Down Expand Up @@ -538,6 +544,9 @@ edit_buffer_insert_ahead (edit_buffer_t *buf, int c)

// update file length
buf->size++;

if (c == '\r' || c == '\n')
buf->lb_dirty = TRUE;
}

/* --------------------------------------------------------------------------------------------- */
Expand Down Expand Up @@ -577,6 +586,9 @@ edit_buffer_delete (edit_buffer_t *buf)
// update file length
buf->size--;

if (c == '\r' || c == '\n')
buf->lb_dirty = TRUE;

return c;
}

Expand Down Expand Up @@ -617,6 +629,9 @@ edit_buffer_backspace (edit_buffer_t *buf)
// update file length
buf->size--;

if (c == '\r' || c == '\n')
buf->lb_dirty = TRUE;

return c;
}

Expand Down Expand Up @@ -790,6 +805,9 @@ edit_buffer_read_file (edit_buffer_t *buf, int fd, off_t size,
}
}

// the loaded content may contain line breaks
buf->lb_dirty = TRUE;

return ret;
}

Expand Down Expand Up @@ -895,3 +913,125 @@ edit_buffer_calc_percent (const edit_buffer_t *buf, off_t offset)
}

/* --------------------------------------------------------------------------------------------- */
/**
* Get the start offset of the trailing whitespace (spaces and tabs) run of the
* line that begins at the given offset. If the line has no trailing whitespace
* the end of the line content is returned, so no character of the line is at or
* after the result.
*
* @param buf editor buffer
* @param bol offset of the first character of the line
*
* @return start offset of the trailing whitespace run (or end of line content)
*/

off_t
edit_buffer_trailing_ws_start (const edit_buffer_t *buf, off_t bol)
{
off_t tws, eol;

eol = edit_buffer_get_eol (buf, bol);

// a CRLF line break's content ends at the "\r", not at the "\n"
if (eol > bol && edit_buffer_is_crlf (buf, eol - 1))
eol--;

for (tws = eol; tws > bol; tws--)
{
int c;

c = edit_buffer_get_byte (buf, tws - 1);
if (c != ' ' && c != '\t')
break;
}

return tws;
}

/* --------------------------------------------------------------------------------------------- */
/**
* Detect the line break type used in the buffer content.
*
* @param buf editor buffer
*
* @return LB_WIN if all line breaks are "\r\n"; LB_UNIX if all line breaks
* are "\n" (including buffers without any line breaks); LB_MAC if
* all line breaks are "\r"; LB_ASIS if the buffer uses a mixture of
* different line breaks (such a file can only be saved as-is)
*/

LineBreaks
edit_buffer_detect_line_breaks (const edit_buffer_t *buf)
{
off_t crlf = 0; // "\r\n" line breaks
off_t cr = 0; // "\r" line breaks not followed by "\n"
off_t lf = 0; // "\n" line breaks not preceded by "\r"
off_t i;

for (i = 0; i < buf->size; i++)
{
if (edit_buffer_get_byte (buf, i) == '\r')
{
if (edit_buffer_is_crlf (buf, i))
crlf++;
else
cr++;
}
else if (edit_buffer_get_byte (buf, i) == '\n'
&& (i == 0 || edit_buffer_get_byte (buf, i - 1) != '\r'))
lf++;
}

if (crlf > 0)
{
if (cr > 0 || lf > 0)
return LB_ASIS; // mixture of line breaks
return LB_WIN;
}

if (cr > 0)
{
if (lf > 0)
return LB_ASIS; // mixture of line breaks
return LB_MAC;
}

return LB_UNIX;
}

/* --------------------------------------------------------------------------------------------- */
/**
* Recompute the cached line break type if the line breaks have changed.
* Cheap (O(1)) unless a line break was inserted or deleted since the last
* recomputation.
*
* @param buf pointer to editor buffer
*/

void
edit_buffer_refresh_line_breaks (edit_buffer_t *buf)
{
if (buf->lb_dirty)
{
buf->lb_detected = edit_buffer_detect_line_breaks (buf);
buf->lb_dirty = FALSE;
}
}

/* --------------------------------------------------------------------------------------------- */
/**
* Get the cached line break type of the buffer content.
* Call edit_buffer_refresh_line_breaks() first if the result must be current.
*
* @param buf pointer to editor buffer
*
* @return cached detect_line_breaks() result
*/

LineBreaks
edit_buffer_get_line_breaks (const edit_buffer_t *buf)
{
return buf->lb_detected;
}

/* --------------------------------------------------------------------------------------------- */
27 changes: 27 additions & 0 deletions src/editor/editbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ typedef struct edit_buffer_struct
off_t size; // file size
long lines; // total lines in the file
long curs_line; // line number of the cursor.

LineBreaks lb_detected; // cached detect_line_breaks() result
gboolean lb_dirty; // line breaks have changed, need detect_line_breaks()
} edit_buffer_t;

typedef struct edit_buffer_read_file_status_msg_struct
Expand Down Expand Up @@ -63,6 +66,11 @@ off_t edit_buffer_write_file (edit_buffer_t *buf, int fd);

int edit_buffer_calc_percent (const edit_buffer_t *buf, off_t offset);

off_t edit_buffer_trailing_ws_start (const edit_buffer_t *buf, off_t bol);
LineBreaks edit_buffer_detect_line_breaks (const edit_buffer_t *buf);
void edit_buffer_refresh_line_breaks (edit_buffer_t *buf);
LineBreaks edit_buffer_get_line_breaks (const edit_buffer_t *buf);

/*** inline functions ****************************************************************************/

static inline int
Expand Down Expand Up @@ -109,6 +117,25 @@ edit_buffer_get_current_eol (const edit_buffer_t *buf)
return edit_buffer_get_eol (buf, buf->curs1);
}

/* --------------------------------------------------------------------------------------------- */
/**
* Check whether the buffer contains a Windows ("\r\n") line break
* starting at the specified position.
*
* @param buf editor buffer
* @param byte_index position of the potential "\r" character
*
* @return TRUE if the two bytes at byte_index and byte_index + 1 are "\r" and "\n"
*/

static inline gboolean
edit_buffer_is_crlf (const edit_buffer_t *buf, off_t byte_index)
{
return (byte_index >= 0 && byte_index + 1 < buf->size
&& edit_buffer_get_byte (buf, byte_index) == '\r'
&& edit_buffer_get_byte (buf, byte_index + 1) == '\n');
}

/* --------------------------------------------------------------------------------------------- */

#endif
87 changes: 75 additions & 12 deletions src/editor/editdraw.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,18 +142,59 @@ format_character_code (WEdit *edit)

#undef CHAR_CODE_BUF_SIZE

/* --------------------------------------------------------------------------------------------- */
/**
* Get the cached line break type of the buffer content.
* The result is recomputed only when line breaks have changed.
*/

static inline LineBreaks
edit_get_detected_line_breaks (edit_buffer_t *buf)
{
edit_buffer_refresh_line_breaks (buf);
return edit_buffer_get_line_breaks (buf);
}

/* --------------------------------------------------------------------------------------------- */
/**
* Get the status line sign describing the line break type
* of the buffer content:
* <CRLF> - Windows ("\r\n")
* <CR> - Macintosh ("\r")
* <LF> - Unix ("\n")
* <?> - mixture of line breaks or no line breaks
*/

static inline const char *
edit_line_breaks_status_sign (edit_buffer_t *buf)
{
switch (edit_get_detected_line_breaks (buf))
{
case LB_WIN:
return "<CRLF>";
case LB_MAC:
return "<CR>";
case LB_UNIX:
return "<LF>";
case LB_ASIS:
default:
return "<?>";
}
}

/* --------------------------------------------------------------------------------------------- */

static inline void
status_string (WEdit *edit, char *s, int w)
{
char *character_code;
const char *lb = edit_line_breaks_status_sign (&edit->buffer);

character_code = format_character_code (edit);

// The field lengths just prevent the status line from shortening too much
if (edit_options.simple_statusbar)
g_snprintf (s, w, "%c%c%c%c %3ld %5ld/%ld %6ld/%ld [%s] %s",
g_snprintf (s, w, "%c%c%c%c %3ld %5ld/%ld %6ld/%ld [%s] %s %s",
edit->mark1 != edit->mark2 ? (edit->column_highlight ? 'C' : 'B') : '-', //
edit->modified != 0 ? 'M' : '-', //
macro_index < 0 ? '-' : 'R', //
Expand All @@ -165,9 +206,11 @@ status_string (WEdit *edit, char *s, int w)
(long) edit->buffer.size, //
character_code,
mc_global.source_codepage >= 0 ? get_codepage_id (mc_global.source_codepage)
: "");
: "",
lb //
);
else
g_snprintf (s, w, "[%c%c%c%c] %2ld L:[%3ld+%2ld %3ld/%3ld] *(%-4ld/%4ldb) [%s] %s",
g_snprintf (s, w, "[%c%c%c%c] %2ld L:[%3ld+%2ld %3ld/%3ld] *(%-4ld/%4ldb) [%s] %s %s",
edit->mark1 != edit->mark2 ? (edit->column_highlight ? 'C' : 'B') : '-', //
edit->modified != 0 ? 'M' : '-', //
macro_index < 0 ? '-' : 'R', //
Expand All @@ -181,7 +224,9 @@ status_string (WEdit *edit, char *s, int w)
(long) edit->buffer.size, //
character_code,
mc_global.source_codepage >= 0 ? get_codepage_id (mc_global.source_codepage)
: "");
: "",
lb //
);

g_free (character_code);
}
Expand Down Expand Up @@ -297,6 +342,15 @@ edit_status_window (WEdit *edit)
edit->overwrite == 0 ? '-' : 'O');
}

tty_getyx (&y, &x);
x -= w->rect.x;
x += 4;
if (x + 6 <= cols - 2 - 6)
{
edit_move (x, 0);
tty_printf ("%s", edit_line_breaks_status_sign (&edit->buffer));
}

if (cols > 30)
{
edit_move (2, w->rect.lines - 1);
Expand Down Expand Up @@ -497,11 +551,16 @@ edit_draw_this_line (WEdit *edit, off_t b, long row, long start_col, long end_co
int col, start_col_real;
int abn_style;
int book_mark = 0;
gboolean crlf_hidden;
char line_stat[LINE_STATE_WIDTH + 1] = "\0";

if (row > w->rect.lines - 1 - EDIT_TEXT_VERTICAL_OFFSET - 2 * (edit->fullscreen != 0 ? 0 : 1))
return;

// in a pure Windows file the "\r" of a "\r\n" line break is a line break
// (hidden); in any other file it is shown as "^M"
crlf_hidden = (edit_buffer_get_line_breaks (&edit->buffer) == LB_WIN);

if (book_mark_query_color (edit, edit->start_line + row, EDITOR_BOOKMARK_COLOR))
book_mark = EDITOR_BOOKMARK_COLOR;
else if (book_mark_query_color (edit, edit->start_line + row, EDITOR_BOOKMARK_FOUND_COLOR))
Expand Down Expand Up @@ -554,14 +613,7 @@ edit_draw_this_line (WEdit *edit, off_t b, long row, long start_col, long end_co
off_t tws = 0;

if (edit_options.visible_tws && tty_use_colors ())
for (tws = edit_buffer_get_eol (&edit->buffer, b); tws > b; tws--)
{
unsigned int c;

c = edit_buffer_get_byte (&edit->buffer, tws - 1);
if (!whitespace (c))
break;
}
tws = edit_buffer_trailing_ws_start (&edit->buffer, b);

while (col <= end_col - edit->start_col)
{
Expand Down Expand Up @@ -603,6 +655,13 @@ edit_draw_this_line (WEdit *edit, off_t b, long row, long start_col, long end_co
else
c = edit_buffer_get_byte (&edit->buffer, q);

// the "\r" of a hidden "\r\n" line break is not shown
if (c == '\r' && crlf_hidden && edit_buffer_is_crlf (&edit->buffer, q))
{
q++;
continue;
}

// we don't use bg for mc - fg contains both
if (book_mark != 0)
p->style |= book_mark << 16;
Expand Down Expand Up @@ -817,6 +876,10 @@ render_edit_text (WEdit *edit, long start_row, long start_column, long end_row,
int y1, x1, y2, x2;
int last_line, last_column;

// make sure the cached line break type (used to decide whether a "\r" is
// shown as "^M" or hidden) is up to date before drawing
edit_buffer_refresh_line_breaks (&edit->buffer);

// draw only visible region

last_line = wh->rect.y + wh->rect.lines - 1;
Expand Down
Loading
Loading