• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

thoni56 / c-xrefactory / 1706

11 Jan 2026 10:52PM UTC coverage: 84.87% (+0.03%) from 84.839%
1706

push

travis-ci

thoni56
[docs] Updated comments about checkpoints

13945 of 16431 relevant lines covered (84.87%)

17478881.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.22
src/move_function.c
1
#include "move_function.h"
2

3
#include <assert.h>
4
#include <ctype.h>
5
#include <stdbool.h>
6
#include <stdio.h>
7
#include <stdlib.h>
8
#include <string.h>
9

10
#include "commons.h"
11
#include "cxfile.h"
12
#include "cxref.h"
13
#include "editor.h"
14
#include "editormarker.h"
15
#include "editorbuffer.h"
16
#include "filetable.h"
17
#include "fileio.h"
18
#include "globals.h"
19
#include "misc.h"
20
#include "options.h"
21
#include "parsing.h"
22
#include "ppc.h"
23
#include "proto.h"
24
#include "protocol.h"
25
#include "refactory.h"
26
#include "reference.h"
27
#include "referenceableitemtable.h"
28
#include "stackmemory.h"
29
#include "undo.h"
30

31

32
static void moveMarkerToTheEndOfDefinitionScope(EditorMarker *marker) {
5✔
33
    int offset;
34
    offset = marker->offset;
5✔
35
    moveEditorMarkerToNonBlankOrNewline(marker, 1);
5✔
36
    if (marker->offset >= marker->buffer->allocation.bufferSize) {
5✔
37
        return;
×
38
    }
39
    if (CHAR_ON_MARKER(marker) == '/' && CHAR_AFTER_MARKER(marker) == '/') {
×
40
        if (options.commentMovingMode == CM_NO_COMMENT)
×
41
            return;
×
42
        moveEditorMarkerToNewline(marker, 1);
×
43
        marker->offset++;
×
44
    } else if (CHAR_ON_MARKER(marker) == '/' && CHAR_AFTER_MARKER(marker) == '*') {
×
45
        if (options.commentMovingMode == CM_NO_COMMENT)
×
46
            return;
×
47
        marker->offset++;
×
48
        marker->offset++;
×
49
        while (marker->offset < marker->buffer->allocation.bufferSize &&
×
50
               (CHAR_ON_MARKER(marker) != '*' || CHAR_AFTER_MARKER(marker) != '/')) {
51
            marker->offset++;
×
52
        }
×
53
        if (marker->offset < marker->buffer->allocation.bufferSize) {
×
54
            marker->offset++;
55
            marker->offset++;
×
56
        }
×
57
        offset = marker->offset;
×
58
        moveEditorMarkerToNonBlankOrNewline(marker, 1);
×
59
        if (CHAR_ON_MARKER(marker) == '\n')
60
            marker->offset++;
×
61
        else
5✔
62
            marker->offset = offset;
5✔
63
    } else if (CHAR_ON_MARKER(marker) == '\n') {
64
        marker->offset++;
×
65
    } else {
×
66
        if (options.commentMovingMode == CM_NO_COMMENT)
×
67
            return;
68
        marker->offset = offset;
69
    }
70
}
×
71

72
static MarkerLocationKind markerWRTComment(EditorMarker *marker, int *commentBeginOffset) {
×
73
    char *begin, *end, *mms;
×
74
    assert(marker->buffer && marker->buffer->allocation.text);
×
75
    char *text = marker->buffer->allocation.text;
×
76
    end = text + marker->buffer->allocation.bufferSize;
×
77
    mms = text + marker->offset;
×
78
    while (text < end && text < mms) {
×
79
        begin = text;
80
        if (*text == '/' && (text + 1) < end && *(text + 1) == '*') {
×
81
            // /**/ comment
×
82
            text += 2;
×
83
            while ((text + 1) < end && !(*text == '*' && *(text + 1) == '/'))
×
84
                text++;
×
85
            if (text + 1 < end)
×
86
                text += 2;
×
87
            if (text > mms) {
×
88
                *commentBeginOffset = begin - marker->buffer->allocation.text;
89
                return MARKER_IS_IN_STAR_COMMENT;
×
90
            }
91
        } else if (*text == '/' && text + 1 < end && *(text + 1) == '/') {
×
92
            // // comment
×
93
            text += 2;
×
94
            while (text < end && *text != '\n')
×
95
                text++;
×
96
            if (text < end)
×
97
                text += 1;
×
98
            if (text > mms) {
×
99
                *commentBeginOffset = begin - marker->buffer->allocation.text;
100
                return MARKER_IS_IN_SLASH_COMMENT;
×
101
            }
102
        } else if (*text == '"') {
×
103
            // string, pass it removing all inside (also /**/ comments)
×
104
            text++;
×
105
            while (text < end && *text != '"') {
×
106
                text++;
×
107
                if (*text == '\\') {
×
108
                    text++;
109
                    text++;
110
                }
×
111
            }
×
112
            if (text < end)
113
                text++;
×
114
        } else {
115
            text++;
116
        }
×
117
    }
118
    return MARKER_IS_IN_CODE;
119
}
5✔
120

121
static void moveMarkerToTheBeginOfDefinitionScope(EditorMarker *marker) {
122
    int offsetToBeginning;
5✔
123

5✔
124
    int slashedCommentsProcessed = 0;
×
125
    int staredCommentsProcessed = 0;
5✔
126
    for (;;) {
5✔
127
        offsetToBeginning = marker->offset;
5✔
128
        marker->offset--;
5✔
129
        moveEditorMarkerToNonBlankOrNewline(marker, -1);
4✔
130
        if (CHAR_ON_MARKER(marker) == '\n') {
4✔
131
            offsetToBeginning = marker->offset + 1;
132
            marker->offset--;
5✔
133
        }
5✔
134
        if (options.commentMovingMode == CM_NO_COMMENT)
×
135
            break;
136
        moveEditorMarkerToNonBlank(marker, -1);
×
137
        int comBeginOffset;
×
138
        MarkerLocationKind markerLocationKind = markerWRTComment(marker, &comBeginOffset);
×
139
        if (markerLocationKind == MARKER_IS_IN_CODE)
×
140
            break;
×
141
        else if (markerLocationKind == MARKER_IS_IN_STAR_COMMENT) {
×
142
            if (options.commentMovingMode == CM_SINGLE_SLASHED)
×
143
                break;
×
144
            if (options.commentMovingMode == CM_ALL_SLASHED)
×
145
                break;
×
146
            if (staredCommentsProcessed > 0 && options.commentMovingMode == CM_SINGLE_STARRED)
×
147
                break;
×
148
            if (staredCommentsProcessed > 0 &&
×
149
                options.commentMovingMode == CM_SINGLE_SLASHED_AND_STARRED)
×
150
                break;
×
151
            staredCommentsProcessed++;
152
            marker->offset = comBeginOffset;
153
        }
×
154
        // slash comment, skip them all
×
155
        else if (markerLocationKind == MARKER_IS_IN_SLASH_COMMENT) {
×
156
            if (options.commentMovingMode == CM_SINGLE_STARRED)
×
157
                break;
×
158
            if (options.commentMovingMode == CM_ALL_STARRED)
×
159
                break;
×
160
            if (slashedCommentsProcessed > 0 && options.commentMovingMode == CM_SINGLE_SLASHED)
×
161
                break;
×
162
            if (slashedCommentsProcessed > 0 &&
×
163
                options.commentMovingMode == CM_SINGLE_SLASHED_AND_STARRED)
×
164
                break;
×
165
            slashedCommentsProcessed++;
166
            marker->offset = comBeginOffset;
×
167
        } else {
×
168
            warningMessage(ERR_INTERNAL, "A new comment?");
169
            break;
170
        }
5✔
171
    }
5✔
172
    marker->offset = offsetToBeginning;
173
}
5✔
174

175
static EditorMarker *getTargetFromOptions(void) {
176
    EditorMarker *target;
177
    EditorBuffer *targetBuffer;
178
    int           targetLine;
5✔
179

180
    targetBuffer = findOrCreateAndLoadEditorBufferForFile(
5✔
181
        normalizeFileName_static(refactoringOptions.moveTargetFile, cwd));
×
182
    if (targetBuffer == NULL)
5✔
183
        FATAL_ERROR(ERR_ST, "Could not find a buffer for target position", XREF_EXIT_ERR);
5✔
184
    target = newEditorMarker(targetBuffer, 0);
5✔
185
    sscanf(refactoringOptions.refactor_target_line, "%d", &targetLine);
5✔
186
    moveEditorMarkerToLineAndColumn(target, targetLine, 0);
187
    return target;
188
}
4✔
189

190
static char *extractFunctionSignature(EditorMarker *startMarker, EditorMarker *endMarker) {
4✔
191

4✔
192
    int searchOffset = startMarker->offset;
4✔
193
    int braceOffset = -1;
194
    char *text = startMarker->buffer->allocation.text;
66✔
195

66✔
196
    while (searchOffset < endMarker->offset) {
4✔
197
        if (text[searchOffset] == '{') {
4✔
198
            braceOffset = searchOffset;
199
            break;
62✔
200
        }
201
        searchOffset++;
202
    }
4✔
203

4✔
204
    char *functionSignature = NULL;
205
    if (braceOffset != -1) {
4✔
206
        /* Extract signature (from start to just before '{') */
4✔
207
        int signatureLength = braceOffset - startMarker->offset;
4✔
208
        functionSignature = stackMemoryAlloc(signatureLength + 1);
4✔
209
        strncpy(functionSignature, &text[startMarker->offset], signatureLength);
210
        functionSignature[signatureLength] = '\0';
211

4✔
212
        /* Trim trailing whitespace from signature */
4✔
213
        int i = signatureLength - 1;
8✔
214
        while (i >= 0
4✔
215
               && (functionSignature[i] == ' ' || functionSignature[i] == '\t'
4✔
216
                   || functionSignature[i] == '\n' || functionSignature[i] == '\r')) {
4✔
217
            functionSignature[i] = '\0';
218
            i--;
219
        }
220
    }
4✔
221

222
    return functionSignature;
223
}
4✔
224

4✔
225
static char *findCorrespondingHeaderFile(EditorMarker *target) {
4✔
226
    char *targetFileName = target->buffer->fileName;
4✔
227
    char *suffix = getFileSuffix(targetFileName);
228
    char *headerFileName = NULL;
4✔
229

230
    if (strcmp(suffix, ".c") == 0) {
231
        /* Build header filename by replacing .c with .h */
4✔
232
        char headerPath[MAX_FILE_NAME_SIZE];
233
        int baseLength = suffix - targetFileName; /* Length up to the '.' */
4✔
234

4✔
235
        strncpy(headerPath, targetFileName, baseLength);
4✔
236
        headerPath[baseLength] = '\0';
237
        strcat(headerPath, ".h");
4✔
238

3✔
239
        if (fileExists(headerPath)) {
3✔
240
            headerFileName = stackMemoryAlloc(strlen(headerPath) + 1);
241
            strcpy(headerFileName, headerPath);
242
        }
243
    }
4✔
244

245
    return headerFileName;
246
}
3✔
247

3✔
248
static int findHeaderInsertionPoint(EditorBuffer *headerBuffer) {
3✔
249
    char *text = headerBuffer->allocation.text;
250
    int size = getSizeOfEditorBuffer(headerBuffer);
251

21✔
252
    /* Search backwards for "\n#endif" pattern */
21✔
253
    for (int i = size - 1; i >= 1; i--) {
254
        if (text[i-1] == '\n' && text[i] == '#') {
3✔
255
            /* Found newline followed by # - check if it's #endif */
3✔
256
            if (i + 5 < size && strncmp(&text[i], "#endif", 6) == 0) {
6✔
257
                int offset = i - 1;  /* Start at the '\n' before '#' */
3✔
258
                while (offset > 0 && isspace(text[offset-1])) {
259
                    offset--;
3✔
260
                }
261
                return offset;
262
            }
263
        }
264
    }
265

×
266
    /* Edge case: file starts with #endif (no preceding newline), just insert it first */
×
267
    if (size >= 6 && strncmp(text, "#endif", 6) == 0) {
268
        return 0;
269
    }
270

×
271
    /* No #endif found - insert at end */
272
    return size;
273
}
3✔
274

275
static void insertExternDeclaration(char *functionSignature, EditorBuffer *headerBuffer, int offset) {
276
    /* Build extern declaration: "extern <signature>;\n" */
3✔
277
    char *externDecl =
3✔
278
        stackMemoryAlloc(strlen("\nextern ") + strlen(functionSignature) + strlen(";") + 1);
3✔
279
    strcpy(externDecl, "\nextern ");
3✔
280
    strcat(externDecl, functionSignature);
281
    strcat(externDecl, ";");
282

3✔
283
    /* Insert at the beginning of the header file */
3✔
284
    replaceStringInEditorBuffer(headerBuffer, offset, 0, externDecl, strlen(externDecl), &editorUndo);
285
}
4✔
286

4✔
287
static bool lookingAtStatic(char *text, int remaining) {
288
    return remaining >= strlen("static ") && strncmp(text, "static", 6) == 0
289
        && (text[6] == ' ' || text[6] == '\t');
4✔
290
}
4✔
291

4✔
292
static int removeStaticKeywordIfPresent(EditorMarker *startMarker, EditorMarker *point, int size) {
4✔
293
    EditorMarker *searchMarker = newEditorMarker(startMarker->buffer, startMarker->offset);
294
    EditorMarker *staticMarker = NULL;
4✔
295
    bool foundStatic = false;
4✔
296

4✔
297
    while (searchMarker->offset < point->offset) {
298
        char *text = &startMarker->buffer->allocation.text[searchMarker->offset];
299
        int remaining = point->offset - searchMarker->offset;
4✔
300

4✔
301
        /* Check if we're at "static " (with space or tab after) */
4✔
302
        if (lookingAtStatic(text, remaining)) {
4✔
303
            foundStatic = true;
304
            staticMarker = newEditorMarker(startMarker->buffer, searchMarker->offset);
×
305
            break;
306
        }
4✔
307
        searchMarker->offset++;
308
    }
4✔
309
    freeEditorMarker(searchMarker);
4✔
310

311
    if (foundStatic) {
4✔
312
        replaceStringInEditorBuffer(staticMarker->buffer, staticMarker->offset, strlen("static "),
313
                                    "", 0, &editorUndo);
4✔
314
        freeEditorMarker(staticMarker);
315
        /* Adjust size since we removed "static " */
316
        size -= strlen("static ");
4✔
317
    }
318

319
    return size;
3✔
320
}
3✔
321

3✔
322
static bool sourceAlreadyIncludesHeader(EditorBuffer *sourceBuffer, char *headerFileName) {
323
    int sourceFileNumber = sourceBuffer->fileNumber;
3✔
324
    int headerFileNumber = getFileNumberFromName(headerFileName);
×
325

326
    if (headerFileNumber == NO_FILE_NUMBER) {
327
        return false;  /* Header file not in file table yet */
328
    }
3✔
329

330
    /* Ensure include references are loaded from database */
331
    ensureReferencesAreLoadedFor(LINK_NAME_INCLUDE_REFS);
3✔
332

333
    /* Create search item for the header file */
334
    ReferenceableItem searchItem = makeReferenceableItem(LINK_NAME_INCLUDE_REFS, TypeCppInclude,
335
                                                         StorageExtern, GlobalScope, VisibilityGlobal,
336
                                                         headerFileNumber);
337

3✔
338
    /* Look it up in the reference table */
339
    ReferenceableItem *foundItem;
4✔
340
    if (isMemberInReferenceableItemTable(&searchItem, NULL, &foundItem)) {
4✔
341
        /* Check if source file appears in the references */
2✔
342
        for (Reference *ref = foundItem->references; ref != NULL; ref = ref->next) {
343
            if (ref->position.file == sourceFileNumber && ref->usage == UsageUsed) {
344
                return true;  /* source file already includes header! */
345
            }
346
        }
1✔
347
    }
348

349
    return false;  /* Include not found */
1✔
350
}
1✔
351

1✔
352
static int findIncludeInsertionPoint(EditorBuffer *buffer) {
1✔
353
    char *text = buffer->allocation.text;
354
    int size = getSizeOfEditorBuffer(buffer);
355
    int lastIncludeEnd = 0;  /* Offset after last include, or 0 if none found */
63✔
356

357
    /* Scan forward looking for #include directives */
62✔
358
    for (int i = 0; i < size - 8; i++) {  /* 8 = strlen("#include") */
1✔
359
        /* Look for newline followed by #include (or start of file) */
360
        if ((i == 0 || text[i-1] == '\n') && text[i] == '#') {
1✔
361
            if (strncmp(&text[i], "#include", 8) == 0) {
12✔
362
                /* Found an include - find the end of this line */
11✔
363
                int j = i + 8;
364
                while (j < size && text[j] != '\n') {
1✔
365
                    j++;
1✔
366
                }
367
                if (j < size) {
368
                    lastIncludeEnd = j + 1;  /* Offset after the newline */
369
                }
370
            }
371
        }
1✔
372
    }
373

374
    return lastIncludeEnd;  /* 0 if no includes found = insert at start */
375
}
5✔
376

377

5✔
378
static void moveStaticFunctionAndMakeItExtern(EditorMarker *startMarker, EditorMarker *point,
5✔
379
                                              EditorMarker *endMarker, EditorMarker *target) {
×
380
    int functionBlockSize = endMarker->offset - startMarker->offset;
×
381
    EditorBuffer *sourceBuffer = startMarker->buffer;
×
382
    if (target->buffer == startMarker->buffer && target->offset > startMarker->offset &&
383
        target->offset < startMarker->offset + functionBlockSize) {
384
        ppcGenRecord(PPC_INFORMATION, "You can't move something into itself.");
385
        return;
386
    }
5✔
387

388
    /* Check if function has "static" keyword and remove it when moving between files.
5✔
389
     * When moving within the same file, keep static (visibility doesn't change). */
4✔
390
    bool movingBetweenFiles = (startMarker->buffer != target->buffer);
391

392
    if (movingBetweenFiles) {
393
        functionBlockSize = removeStaticKeywordIfPresent(startMarker, point, functionBlockSize);
5✔
394
    }
5✔
395

396
    /* Extract function signature for extern declaration (before moving) */
4✔
397
    char *functionSignature = NULL;
398
    if (movingBetweenFiles) {
399
        /* Find the opening brace to determine where signature ends */
400
        functionSignature = extractFunctionSignature(startMarker, endMarker);
5✔
401
    }
402

403
    /* Now move the (possibly modified) function block */
5✔
404
    moveBlockInEditorBuffer(startMarker, target, functionBlockSize, &editorUndo);
5✔
405

4✔
406
    /* After moving the function, check if we need to add an extern declaration to the header */
407
    char *headerFileName = NULL;
408
    if (movingBetweenFiles) {
409
        headerFileName = findCorrespondingHeaderFile(target);
5✔
410
    }
3✔
411

3✔
412
    /* Insert extern declaration into header file if we have both header and signature */
3✔
413
    if (headerFileName != NULL && functionSignature != NULL) {
414
        EditorBuffer *headerBuffer = findOrCreateAndLoadEditorBufferForFile(headerFileName);
415
        int insertionOffset = findHeaderInsertionPoint(headerBuffer);
3✔
416
        insertExternDeclaration(functionSignature, headerBuffer, insertionOffset);
417

1✔
418
        /* Check if source file needs to include the header */
1✔
419
        if (!sourceAlreadyIncludesHeader(sourceBuffer, headerFileName)) {
1✔
420
            /* Build the include directive */
1✔
421
            char *baseHeaderName = simpleFileName(headerFileName);
1✔
422
            char *includeDirective = stackMemoryAlloc(strlen("#include \"") + strlen(baseHeaderName) + strlen("\"\n") + 1);
423
            strcpy(includeDirective, "#include \"");
424
            strcat(includeDirective, baseHeaderName);
1✔
425
            strcat(includeDirective, "\"\n");
1✔
426

427
            /* Insert at beginning of source file */
428
            int insertionOffset = findIncludeInsertionPoint(sourceBuffer);
429
            replaceStringInEditorBuffer(sourceBuffer, insertionOffset, 0, includeDirective, strlen(includeDirective), &editorUndo);
430
        }
5✔
431
    }
5✔
432
}
433

5✔
434
void moveFunction(EditorMarker *point) {
×
435
    EditorMarker *target = getTargetFromOptions();
×
436

437
    if (!isValidMoveTarget(target)) {
438
        errorMessage(ERR_ST, "Invalid target place");
5✔
439
        return;
440
    }
5✔
441

442
    ensureReferencesAreUpdated(refactoringOptions.project);
5✔
443

×
444
    FunctionBoundariesResult bounds = getFunctionBoundaries(point);
445

446
    if (!bounds.found) {
447
        FATAL_ERROR(ERR_INTERNAL, "Can't find declaration coordinates", XREF_EXIT_ERR);
5✔
448
    }
5✔
449

5✔
450
    /* Convert positions to markers and adjust for definition scope */
5✔
451
    EditorMarker *functionStart = newEditorMarkerForPosition(bounds.functionBegin);
452
    EditorMarker *functionEnd = newEditorMarkerForPosition(bounds.functionEnd);
5✔
453
    moveMarkerToTheBeginOfDefinitionScope(functionStart);
454
    moveMarkerToTheEndOfDefinitionScope(functionEnd);
5✔
455

456
    int lines = countLinesBetweenEditorMarkers(functionStart, functionEnd);
457

5✔
458
    moveStaticFunctionAndMakeItExtern(functionStart, point, functionEnd, target);
5✔
459

5✔
460
    // and generate output
461
    applyWholeRefactoringFromUndo();
462
    ppcGotoMarker(point);
463
    ppcValueRecord(PPC_INDENT, lines, "");
464
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc