1414 *******************************************************************************/
1515package org .eclipse .cdt .internal .ui .editor ;
1616
17+ import java .util .ArrayDeque ;
18+ import java .util .Deque ;
1719import java .util .LinkedList ;
1820import java .util .List ;
1921
2931import org .eclipse .cdt .core .dom .ast .IASTIfStatement ;
3032import org .eclipse .cdt .core .dom .ast .IASTNode ;
3133import org .eclipse .cdt .core .dom .ast .IASTNodeSelector ;
34+ import org .eclipse .cdt .core .dom .ast .IASTPreprocessorElifStatement ;
35+ import org .eclipse .cdt .core .dom .ast .IASTPreprocessorElseStatement ;
36+ import org .eclipse .cdt .core .dom .ast .IASTPreprocessorEndifStatement ;
37+ import org .eclipse .cdt .core .dom .ast .IASTPreprocessorIfStatement ;
38+ import org .eclipse .cdt .core .dom .ast .IASTPreprocessorIfdefStatement ;
39+ import org .eclipse .cdt .core .dom .ast .IASTPreprocessorIfndefStatement ;
40+ import org .eclipse .cdt .core .dom .ast .IASTPreprocessorStatement ;
3241import org .eclipse .cdt .core .dom .ast .IASTReturnStatement ;
3342import org .eclipse .cdt .core .dom .ast .IASTStatement ;
3443import org .eclipse .cdt .core .dom .ast .IASTSwitchStatement ;
@@ -68,7 +77,7 @@ public class CSourceStickyLinesProvider implements IStickyLinesProvider {
6877 public List <IStickyLine > getStickyLines (ISourceViewer sourceViewer , int lineNumber ,
6978 StickyLinesProperties properties ) {
7079 final long startTime = System .currentTimeMillis ();
71- final LinkedList <IStickyLine > stickyLines = new LinkedList <>();
80+ List <IStickyLine > stickyLines = new LinkedList <>();
7281 // lineNumber in the zero-based line number as known to the IDocument
7382 // textWidgetLineNumber is the the line number as known to the ISourceViewer
7483 final int textWidgetLineNumber = mapLineNumberToWidget (sourceViewer , lineNumber );
@@ -119,23 +128,33 @@ public List<IStickyLine> getStickyLines(ISourceViewer sourceViewer, int lineNumb
119128 try {
120129 final int offset = sourceViewer .getDocument ().getLineOffset (lineNumber );
121130 node = nodeSelector .findEnclosingNode (offset , line .length ());
131+ if (node instanceof IASTPreprocessorStatement ) {
132+ // find enclosing non-preprocessor node using adjusted offset
133+ final int nodeOffset = node .getFileLocation ().getNodeOffset ();
134+ node = nodeSelector .findEnclosingNode (nodeOffset - 1 , 2 );
135+ }
122136 } catch (BadLocationException e ) {
123137 ILog .get ().error ("Error getting line offset for sticky lines" , e ); //$NON-NLS-1$
124138 return stickyLines ;
125139 }
126140
127141 // process sticky ancestor nodes
142+ final LinkedList <IStickyLine > ancestorLines = new LinkedList <>();
128143 while (null != node ) {
129144 if (DEBUG ) {
130145 System .out .printf ("> Examining AST node: %s (lines %d-%d)\n " , node .getClass ().getSimpleName (), //$NON-NLS-1$
131146 node .getFileLocation ().getStartingLineNumber (),
132147 node .getFileLocation ().getEndingLineNumber ());
133148 }
134149 if (nodeInstanceOfStickyClass (node )) {
135- processStickyNode (node , fileLineNumber , sourceViewer , stickyLines );
150+ processStickyNode (node , fileLineNumber , sourceViewer , ancestorLines );
136151 }
137152 node = node .getParent ();
138153 }
154+
155+ // process sticky pre-processor nodes and merge with ancestor nodes
156+ final List <IStickyLine > preprocessorLines = findPreprocessorStickyLines (fileLineNumber , sourceViewer );
157+ stickyLines = mergeStickyLines (ancestorLines , preprocessorLines );
139158 }
140159 if (DEBUG ) {
141160 System .out .println ("> Sticky line count: " + stickyLines .size ()); //$NON-NLS-1$
@@ -144,6 +163,45 @@ public List<IStickyLine> getStickyLines(ISourceViewer sourceViewer, int lineNumb
144163 return stickyLines ;
145164 }
146165
166+ private List <IStickyLine > findPreprocessorStickyLines (int fileLineNumber , ISourceViewer sourceViewer ) {
167+ final Deque <IASTPreprocessorStatement > stack = new ArrayDeque <>();
168+ for (IASTPreprocessorStatement statement : fAst .getAllPreprocessorStatements ()) {
169+ if (statement .getFileLocation ().getStartingLineNumber () >= fileLineNumber ) {
170+ break ;
171+ }
172+ if (nodeInstanceOfPreprocessorIfClass (statement ) || statement instanceof IASTPreprocessorElifStatement
173+ || statement instanceof IASTPreprocessorElseStatement ) {
174+ stack .push (statement );
175+ } else if (statement instanceof IASTPreprocessorEndifStatement ) {
176+ IASTPreprocessorStatement previous = statement ;
177+ while (!stack .isEmpty () && !nodeInstanceOfPreprocessorIfClass (previous )) {
178+ previous = stack .pop ();
179+ }
180+ }
181+ }
182+ final LinkedList <IStickyLine > stickyLines = new LinkedList <>();
183+ stack .forEach (statement -> addStickyLine (statement .getFileLocation ().getStartingLineNumber (), sourceViewer ,
184+ stickyLines ));
185+ return stickyLines ;
186+ }
187+
188+ private List <IStickyLine > mergeStickyLines (List <IStickyLine > lines1 , List <IStickyLine > lines2 ) {
189+ final Deque <IStickyLine > deque1 = new ArrayDeque <>(lines1 );
190+ final Deque <IStickyLine > deque2 = new ArrayDeque <>(lines2 );
191+ final List <IStickyLine > mergedLines = new LinkedList <>();
192+ while (null != deque1 .peek () && null != deque2 .peek ()) {
193+ // assume both lists are sorted in ascending line number order
194+ if (deque1 .peek ().getLineNumber () < deque2 .peek ().getLineNumber ()) {
195+ mergedLines .add (deque1 .pop ());
196+ } else {
197+ mergedLines .add (deque2 .pop ());
198+ }
199+ }
200+ mergedLines .addAll (deque1 );
201+ mergedLines .addAll (deque2 );
202+ return mergedLines ;
203+ }
204+
147205 private void processStickyNode (IASTNode node , int fileLineNumber , ISourceViewer sourceViewer ,
148206 LinkedList <IStickyLine > stickyLines ) {
149207 final int startingLineNumber = node .getFileLocation ().getStartingLineNumber ();
@@ -224,6 +282,11 @@ private boolean nodeInstanceOfStickyClass(IASTNode node) {
224282 return STICKY_NODE_CLASSES .stream ().anyMatch (nodeClass -> nodeClass .isInstance (node ));
225283 }
226284
285+ private boolean nodeInstanceOfPreprocessorIfClass (IASTNode node ) {
286+ return node instanceof IASTPreprocessorIfStatement || node instanceof IASTPreprocessorIfdefStatement
287+ || node instanceof IASTPreprocessorIfndefStatement ;
288+ }
289+
227290 private ICElement getInputElement (IEditorPart part ) {
228291 final IEditorInput editorInput = part .getEditorInput ();
229292 if (null != editorInput ) {
0 commit comments