blob: 88b21edb70fd77ec0ba64088ef0b1338b057eeb9 [file] [log] [blame]
/**
* @file align_stack.cpp
* Manages a align stack, which is just a pair of chunk stacks.
* There can be at most 1 item per line in the stack.
* The seqnum is actually a line counter.
*
* @author Ben Gardner
* @license GPL v2+
*/
#include "align_stack.h"
#include "prototypes.h"
#include "chunk_list.h"
/**
* Resets the two ChunkLists and zeroes local vars.
*
* @param span The row span limit
* @param thresh The column threshold
*/
void AlignStack::Start(int span, int thresh)
{
LOG_FMT(LAS, "Start(%d, %d)\n", span, thresh);
m_aligned.Reset();
m_skipped.Reset();
m_span = span;
m_thresh = thresh;
m_min_col = 9999;
m_max_col = 0;
m_nl_seqnum = 0;
m_seqnum = 0;
m_gap = 0;
m_right_align = false;
m_star_style = SS_IGNORE;
m_amp_style = SS_IGNORE;
}
/**
* Calls Add on all the skipped items
*/
void AlignStack::ReAddSkipped()
{
if (!m_skipped.Empty())
{
/* Make a copy of the ChunkStack and clear m_skipped */
m_scratch.Set(m_skipped);
m_skipped.Reset();
const ChunkStack::Entry *ce;
/* Need to add them in order so that m_nl_seqnum is correct */
for (int idx = 0; idx < m_scratch.Len(); idx++)
{
ce = m_scratch.Get(idx);
LOG_FMT(LAS, "ReAddSkipped [%d] - ", ce->m_seqnum);
Add(ce->m_pc, ce->m_seqnum);
}
/* Check to see if we need to flush right away */
NewLines(0);
}
}
/**
* Adds an entry to the appropriate stack.
*
* @param pc The chunk
* @param seqnum Optional seqnum (0=assign one)
*/
void AlignStack::Add(chunk_t *start, int seqnum)
{
LOG_FUNC_ENTRY();
/* Assign a seqnum if needed */
if (seqnum == 0)
{
seqnum = m_seqnum;
}
chunk_t *ali;
chunk_t *ref;
chunk_t *tmp;
chunk_t *prev;
chunk_t *next;
int col_adj = 0; /* Amount the column is shifted for 'dangle' mode */
int tmp_col;
int endcol;
int gap;
m_last_added = 0;
/* Check threshold limits */
if ((m_max_col == 0) || (m_thresh == 0) ||
(((start->column + m_gap) <= (m_max_col + m_thresh)) &&
(((start->column + m_gap) >= (m_max_col - m_thresh)) ||
(start->column >= m_min_col))))
{
/* we are adding it, so update the newline seqnum */
if (seqnum > m_nl_seqnum)
{
m_nl_seqnum = seqnum;
}
/**
* SS_IGNORE: no special handling of '*' or '&', only 'foo' is aligned
* void foo; // gap=5, 'foo' is aligned
* char * foo; // gap=3, 'foo' is aligned
* foomatic foo; // gap=1, 'foo' is aligned
* The gap is the columns between 'foo' and the previous token.
* [void - foo], ['*' - foo], etc
*
* SS_INCLUDE: - space between variable and '*' or '&' is eaten
* void foo; // gap=5, 'foo' is aligned
* char *foo; // gap=5, '*' is aligned
* foomatic foo; // gap=1, 'foo' is aligned
* The gap is the columns between the first '*' or '&' before foo
* and the previous token. [void - foo], [char - '*'], etc
*
* SS_DANGLE: - space between variable and '*' or '&' is eaten
* void foo; // gap=5
* char *bar; // gap=5, as the '*' doesn't count
* foomatic foo; // gap=1
* The gap is the columns between 'foo' and the chunk before the first
* '*' or '&'. [void - foo], [char - bar], etc
*
* If the gap < m_gap, then the column is bumped out by the difference.
* So, if m_gap is 2, then the above would be:
* SS_IGNORE:
* void foo; // gap=6
* char * foo; // gap=4
* foomatic foo; // gap=2
* SS_INCLUDE:
* void foo; // gap=6
* char *foo; // gap=6
* foomatic foo; // gap=2
* SS_DANGLE:
* void foo; // gap=6
* char *bar; // gap=6, as the '*' doesn't count
* foomatic foo; // gap=2
* Right aligned numbers:
* #define A -1
* #define B 631
* #define C 3
* Left aligned numbers:
* #define A -1
* #define B 631
* #define C 3
*
* In the code below, pc is set to the item that is aligned.
* In the above examples, that is 'foo', '*', '-', or 63.
*
* Ref is set to the last part of the type.
* In the above examples, that is 'void', 'char', 'foomatic', 'A', or 'B'.
*
* The '*' and '&' can float between the two.
*
* If align_on_tabstop=true, then SS_DANGLE is changed to SS_INCLUDE.
*/
if (cpd.settings[UO_align_on_tabstop].b && (m_star_style == SS_DANGLE))
{
m_star_style = SS_INCLUDE;
}
/* Find ref. Back up to the real item that is aligned. */
prev = start;
while (((prev = chunk_get_prev(prev)) != NULL) &&
(chunk_is_star(prev) ||
chunk_is_addr(prev) ||
(prev->type == CT_TPAREN_OPEN)))
{
/* do nothing - we want prev when this exits */
}
ref = prev;
if (chunk_is_newline(ref))
{
ref = chunk_get_next(ref);
}
/* Find the item that we are going to align. */
ali = start;
if (m_star_style != SS_IGNORE)
{
/* back up to the first '*' preceding the token */
prev = chunk_get_prev(ali);
while (chunk_is_star(prev))
{
ali = prev;
prev = chunk_get_prev(ali);
}
if (chunk_is_token(prev, CT_TPAREN_OPEN))
{
ali = prev;
prev = chunk_get_prev(ali);
}
}
if (m_amp_style != SS_IGNORE)
{
/* back up to the first '&' preceding the token */
prev = chunk_get_prev(ali);
while (chunk_is_addr(prev))
{
ali = prev;
prev = chunk_get_prev(ali);
}
}
/* Tighten down the spacing between ref and start */
if (!cpd.settings[UO_align_keep_extra_space].b)
{
tmp_col = ref->column;
tmp = ref;
while (tmp != start)
{
next = chunk_get_next(tmp);
tmp_col += space_col_align(tmp, next);
if (next->column != tmp_col)
{
align_to_column(next, tmp_col);
}
tmp = next;
}
}
/* Set the column adjust and gap */
col_adj = 0;
gap = 0;
if (ref != ali)
{
gap = ali->column - (ref->column + ref->len());
}
tmp = ali;
if (chunk_is_token(tmp, CT_TPAREN_OPEN))
{
tmp = chunk_get_next(tmp);
}
if ((chunk_is_star(tmp) && (m_star_style == SS_DANGLE)) ||
(chunk_is_addr(tmp) && (m_amp_style == SS_DANGLE)))
{
col_adj = start->column - ali->column;
gap = start->column - (ref->column + ref->len());
}
/* See if this pushes out the max_col */
endcol = ali->column + col_adj;
if (gap < m_gap)
{
endcol += m_gap - gap;
}
// LOG_FMT(LSYS, "[%p] line %d pc='%s' [%s] col:%d ali='%s' [%s] col:%d ref='%s' [%s] col:%d col_adj=%d endcol=%d, ss=%d as=%d, gap=%d\n",
// this,
// start->orig_line,
// start->str.c_str(), get_token_name(start->type), start->column,
// ali->str.c_str(), get_token_name(ali->type), ali->column,
// ref->str.c_str(), get_token_name(ref->type), ref->column,
// col_adj, endcol, m_star_style, m_amp_style, gap);
ali->align.col_adj = col_adj;
ali->align.ref = ref;
ali->align.start = start;
m_aligned.Push_Back(ali, seqnum);
m_last_added = 1;
LOG_FMT(LAS, "Add-[%s]: line %d, col %d, adj %d : ref=[%s] endcol=%d\n",
ali->str.c_str(), ali->orig_line, ali->column, ali->align.col_adj,
ref->str.c_str(), endcol);
if (m_min_col > endcol)
{
m_min_col = endcol;
}
if (endcol > m_max_col)
{
LOG_FMT(LAS, "Add-aligned [%d/%d/%d]: line %d, col %d : max_col old %d, new %d - min_col %d\n",
seqnum, m_nl_seqnum, m_seqnum,
ali->orig_line, ali->column, m_max_col, endcol, m_min_col);
m_max_col = endcol;
/**
* If there were any entries that were skipped, re-add them as they
* may now be within the threshold
*/
if (!m_skipped.Empty())
{
ReAddSkipped();
}
}
else
{
LOG_FMT(LAS, "Add-aligned [%d/%d/%d]: line %d, col %d : col %d <= %d - min_col %d\n",
seqnum, m_nl_seqnum, m_seqnum,
ali->orig_line, ali->column, endcol, m_max_col, m_min_col);
}
}
else
{
/* The threshold check failed, so add it to the skipped list */
m_skipped.Push_Back(start, seqnum);
m_last_added = 2;
LOG_FMT(LAS, "Add-skipped [%d/%d/%d]: line %d, col %d <= %d + %d\n",
seqnum, m_nl_seqnum, m_seqnum,
start->orig_line, start->column, m_max_col, m_thresh);
}
}
/**
* Adds some newline and calls Flush() if needed
*/
void AlignStack::NewLines(int cnt)
{
if (!m_aligned.Empty())
{
m_seqnum += cnt;
if (m_seqnum > (m_nl_seqnum + m_span))
{
LOG_FMT(LAS, "Newlines<%d>-", cnt);
Flush();
}
else
{
LOG_FMT(LAS, "Newlines<%d>\n", cnt);
}
}
}
/**
* Aligns all the stuff in m_aligned.
* Re-adds 'newer' items in m_skipped.
*/
void AlignStack::Flush()
{
int last_seqnum = 0;
int idx;
int tmp_col;
const ChunkStack::Entry *ce = NULL;
chunk_t *pc;
LOG_FMT(LAS, "Flush (min=%d, max=%d)\n", m_min_col, m_max_col);
m_last_added = 0;
m_max_col = 0;
/* Recalculate the max_col - it may have shifted since the last Add() */
for (idx = 0; idx < m_aligned.Len(); idx++)
{
pc = m_aligned.Get(idx)->m_pc;
/* Set the column adjust and gap */
int col_adj = 0;
int gap = 0;
if (pc != pc->align.ref)
{
gap = pc->column - (pc->align.ref->column + pc->align.ref->len());
}
chunk_t *tmp = pc;
if (tmp->type == CT_TPAREN_OPEN)
{
tmp = chunk_get_next(tmp);
}
if ((chunk_is_star(tmp) && (m_star_style == SS_DANGLE)) ||
(chunk_is_addr(tmp) && (m_amp_style == SS_DANGLE)))
{
col_adj = pc->align.start->column - pc->column;
gap = pc->align.start->column - (pc->align.ref->column + pc->align.ref->len());
}
if (m_right_align)
{
/* Adjust the width for signed numbers */
int start_len = pc->align.start->len();
if (pc->align.start->type == CT_NEG)
{
tmp = chunk_get_next(pc->align.start);
if ((tmp != NULL) && (tmp->type == CT_NUMBER))
{
start_len += tmp->len();
}
}
col_adj += start_len;
}
pc->align.col_adj = col_adj;
/* See if this pushes out the max_col */
int endcol = pc->column + col_adj;
if (gap < m_gap)
{
endcol += m_gap - gap;
}
if (endcol > m_max_col)
{
m_max_col = endcol;
}
}
if (cpd.settings[UO_align_on_tabstop].b && (m_aligned.Len() > 1))
{
m_max_col = align_tab_column(m_max_col);
}
for (idx = 0; idx < m_aligned.Len(); idx++)
{
ce = m_aligned.Get(idx);
pc = ce->m_pc;
tmp_col = m_max_col - pc->align.col_adj;
if (idx == 0)
{
if (m_skip_first && (pc->column != tmp_col))
{
LOG_FMT(LAS, "%s: %d:%d dropping first item due to skip_first\n", __func__,
pc->orig_line, pc->orig_col);
m_skip_first = false;
m_aligned.Pop_Front();
Flush();
m_skip_first = true;
return;
}
pc->flags |= PCF_ALIGN_START;
pc->align.right_align = m_right_align;
pc->align.amp_style = (int)m_amp_style;
pc->align.star_style = (int)m_star_style;
}
pc->align.gap = m_gap;
pc->align.next = m_aligned.GetChunk(idx + 1);
/* Indent the token, taking col_adj into account */
LOG_FMT(LAS, "%s: line %d: '%s' to col %d (adj=%d)\n", __func__,
pc->orig_line, pc->str.c_str(), tmp_col, pc->align.col_adj);
align_to_column(pc, tmp_col);
}
if (ce != NULL)
{
last_seqnum = ce->m_seqnum;
m_aligned.Reset();
}
m_min_col = 9999;
m_max_col = 0;
if (m_skipped.Empty())
{
/* Nothing was skipped, sync the seqnums */
m_nl_seqnum = m_seqnum;
}
else
{
/* Remove all items with seqnum < last_seqnum */
for (idx = 0; idx < m_skipped.Len(); idx++)
{
if (m_skipped.Get(idx)->m_seqnum < last_seqnum)
{
m_skipped.Zap(idx);
}
}
m_skipped.Collapse();
/* Add all items from the skipped list */
ReAddSkipped();
}
}
/**
* Resets the stack, discarding anything that was previously added
*/
void AlignStack::Reset()
{
m_aligned.Reset();
m_skipped.Reset();
}
/**
* Aligns everything else and resets the lists.
*/
void AlignStack::End()
{
if (!m_aligned.Empty())
{
LOG_FMT(LAS, "End-");
Flush();
}
m_aligned.Reset();
m_skipped.Reset();
}