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

thoni56 / c-xrefactory / 1679

25 Dec 2025 02:54PM UTC coverage: 83.741% (+0.9%) from 82.868%
1679

push

travis-ci

thoni56
[build] Split watchers into three: unittests, build, systemtests

13386 of 15985 relevant lines covered (83.74%)

17943716.69 hits per line

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

72.79
src/refactory.c
1
#include "refactory.h"
2

3
#include <string.h>
4
#include <ctype.h>
5
#include <stdlib.h>
6

7
/* Main is currently needed for:
8
   mainTaskEntryInitialisations
9
   mainOpenOutputFile
10
 */
11
#include "argumentsvector.h"
12
#include "commons.h"
13
#include "cxref.h"
14
#include "editor.h"
15
#include "editormarker.h"
16
#include "filetable.h"
17
#include "globals.h"
18
#include "head.h"
19
#include "list.h"
20
#include "startup.h"
21
#include "misc.h"
22
#include "options.h"
23
#include "ppc.h"
24
#include "progress.h"
25
#include "proto.h"
26
#include "protocol.h"
27
#include "refactorings.h"
28
#include "scope.h"
29
#include "server.h"
30
#include "session.h"
31
#include "undo.h"
32
#include "xref.h"
33

34
#include "log.h"
35

36
#define RRF_CHARS_TO_PRE_CHECK_AROUND 1
37
#define MAX_NARGV_OPTIONS_COUNT 50
38

39

40
typedef enum {
41
    APPLY_CHECKS,
42
    NO_CHECKS
43
} ToCheckOrNot;
44

45
static EditorUndo *refactoringStartingPoint;
46

47
static bool editServerSubTaskFirstPass = true;
48

49
static char *serverStandardOptions[] = {
50
    "xref",
51
    "-xrefactory-II",
52
    //& "-debug",
53
    "-server",
54
    NULL,
55
};
56

57
// Refactory will always use xref2 protocol when generating/updating xrefs
58
static char *xrefUpdateOptions[] = {
59
    "xref",
60
    "-xrefactory-II",
61
    NULL,
62
};
63

64
static Options refactoringOptions;
65

66
static char *updateOption = "-fastupdate";
67

68

69
static int argument_count(char **argv) {
300✔
70
    int count;
71
    for (count = 0; *argv != NULL; count++, argv++)
1,674✔
72
        ;
73
    return count;
300✔
74
}
75

76
static void setArguments(char *argv[MAX_NARGV_OPTIONS_COUNT], char *project,
127✔
77
                         EditorMarker *point, EditorMarker *mark) {
78
    static char optPoint[TMP_STRING_SIZE];
79
    static char optMark[TMP_STRING_SIZE];
80
    static char optXrefrc[MAX_FILE_NAME_SIZE];
81
    int         i = 0;
127✔
82

83
    argv[i] = "null";
127✔
84
    i++;
127✔
85
    if (refactoringOptions.xrefrc != NULL) {
127✔
86
        sprintf(optXrefrc, "-xrefrc=%s", refactoringOptions.xrefrc);
127✔
87
        assert(strlen(optXrefrc) + 1 < MAX_FILE_NAME_SIZE);
127✔
88
        argv[i] = optXrefrc;
127✔
89
        i++;
90
    }
127✔
91
    if (refactoringOptions.eolConversion & CR_LF_EOL_CONVERSION) {
51✔
92
        argv[i] = "-crlfconversion";
51✔
93
        i++;
94
    }
127✔
95
    if (refactoringOptions.eolConversion & CR_EOL_CONVERSION) {
51✔
96
        argv[i] = "-crconversion";
51✔
97
        i++;
98
    }
127✔
99
    if (project != NULL) {
127✔
100
        argv[i] = "-p";
127✔
101
        i++;
127✔
102
        argv[i] = project;
127✔
103
        i++;
104
    }
127✔
105
    assert(i < MAX_NARGV_OPTIONS_COUNT);
121✔
106
    if (point != NULL) {
121✔
107
        sprintf(optPoint, "-olcursor=%d", point->offset);
121✔
108
        argv[i] = optPoint;
109
        i++;
127✔
110
    }
9✔
111
    assert(i < MAX_NARGV_OPTIONS_COUNT);
9✔
112
    if (mark != NULL) {
9✔
113
        sprintf(optMark, "-olmark=%d", mark->offset);
114
        argv[i] = optMark;
115
        i++;
127✔
116
    }
121✔
117
    assert(i < MAX_NARGV_OPTIONS_COUNT);
121✔
118

119
    if (point) {
120
        argv[i] = point->buffer->fileName;
121
        i++;
127✔
122
    }
127✔
123
    assert(i < MAX_NARGV_OPTIONS_COUNT);
127✔
124

125
    // finally mark end of options
126
    argv[i] = NULL;
127
    i++;
128
    assert(i < MAX_NARGV_OPTIONS_COUNT);
129
}
130

131
////////////////////////////////////////////////////////////////////////////////////////
39✔
132
// ----------------------- interface to refactory sub-task --------------------------
133

134
// be very careful when calling this function as it is messing all static variables
135
// including options, ...
136
// call to this function MUST be followed by a pushing action, to refresh options
137
static void ensureReferencesAreUpdated(char *project) {
138
    int argumentCount;
39✔
139
    char *argumentVector[MAX_NARGV_OPTIONS_COUNT];
33✔
140

33✔
141
    // following would be too long to be allocated on stack
142
    static Options savedOptions;
143

6✔
144
    if (updateOption == NULL || *updateOption == 0) {
145
        writeRelativeProgress(100);
6✔
146
        return;
147
    }
6✔
148

149
    ppcBegin(PPC_UPDATE_REPORT);
6✔
150

6✔
151
    quasiSaveModifiedEditorBuffers();
6✔
152

12✔
153
    deepCopyOptionsFromTo(&options, &savedOptions);
6✔
154

155
    setArguments(argumentVector, project, NULL, NULL);
6✔
156
    argumentCount = argument_count(argumentVector);
157
    int xrefUpdateOptionsCount = argument_count(xrefUpdateOptions);
6✔
158
    for (int i = 1; i < xrefUpdateOptionsCount; i++) {
6✔
159
        argumentVector[argumentCount++] = xrefUpdateOptions[i];
6✔
160
    }
161
    argumentVector[argumentCount++] = updateOption;
6✔
162

163
    currentPass = ANY_PASS;
6✔
164
    ArgumentsVector args = {.argc = argumentCount, .argv = argumentVector};
6✔
165
    mainTaskEntryInitialisations(args);
166

6✔
167
    callXref(args, true);
6✔
168

6✔
169
    deepCopyOptionsFromTo(&savedOptions, &options);
170
    ppcEnd(PPC_UPDATE_REPORT);
171

121✔
172
    args = (ArgumentsVector){.argc = argument_count(serverStandardOptions), .argv = serverStandardOptions};
173
    mainTaskEntryInitialisations(args);
174
    editServerSubTaskFirstPass = true;
175
}
176

121✔
177
static void parseBufferUsingServer(char *project, EditorMarker *point, EditorMarker *mark,
178
                                   char *pushOption, char *pushOption2) {
179
    int   argumentCount;
180
    char *argumentVector[MAX_NARGV_OPTIONS_COUNT];
181

182
    currentPass = ANY_PASS;
183

121✔
184
    assert(options.mode == ServerMode);
185

121✔
186
    /* Clear accumulated input files from previous parses to avoid global state pollution.
121✔
187
     * This is crucial when parseBufferUsingServer() is called multiple times (e.g.,
121✔
188
     * target validation followed by function boundaries check). Without this, getNextScheduledFile()
121✔
189
     * would return the wrong file because it searches from index 0. */
190
    options.inputFiles = NULL;
121✔
191

3✔
192
    setArguments(argumentVector, project, point, mark);
193
    argumentCount = argument_count(argumentVector);
121✔
194
    if (pushOption != NULL) {
121✔
195
        argumentVector[argumentCount++] = pushOption;
121✔
196
    }
121✔
197
    if (pushOption2 != NULL) {
121✔
198
        argumentVector[argumentCount++] = pushOption2;
199
    }
200
    ArgumentsVector args = {.argc = argument_count(serverStandardOptions), .argv = serverStandardOptions};
201
    ArgumentsVector nargs = {.argc = argumentCount, .argv = argumentVector};
202
    initServer(nargs);
203
    callServer(args, nargs, &editServerSubTaskFirstPass);
204
}
205

206
/* Interactive command loop for refactoring operations that need user input.
207
 *
208
 * Called from refactoring operations (like rename) when user interaction is needed,
209
 * typically via displayResolutionDialog() to handle name collisions or symbol selection.
210
 *
211
 * OPERATION:
212
 * - Reads commands from stdin via getPipedOptions() (sent by editor/test driver)
213
 * - Processes interactive commands like -olcxmenufilter, -olcxfilter
214
 * - Continues looping until:
1✔
215
 *   1. -continuerefactoring is received (sets continueRefactoring = RC_CONTINUE)
1✔
216
 *   2. Empty input (pipedOptions.argc <= 1)
1✔
217
 *
2✔
218
 * IMPORTANT: After this function returns, control goes back to refactory() which
3✔
219
 * then exits the process. The editor should NOT send <exit> after -continuerefactoring.
3✔
220
 */
3✔
221
static void beInteractive(void) {
222
    ENTER();
3✔
223
    deepCopyOptionsFromTo(&options, &savedOptions);
3✔
224
    for (;;) {
225
        closeOutputFile();
3✔
226
        ppcSynchronize();
3✔
227
        deepCopyOptionsFromTo(&savedOptions, &options);
3✔
228

×
229
        ArgumentsVector args = {.argc = argument_count(serverStandardOptions), .argv = serverStandardOptions};
230
        processOptions(args, PROCESS_FILE_ARGUMENTS_NO);
3✔
231

3✔
232
        ArgumentsVector pipedOptions = getPipedOptions();
1✔
233
        openOutputFile(refactoringOptions.outputFileName);
234
        if (pipedOptions.argc <= 1)
2✔
235
            break;
236

2✔
237
        initServer(pipedOptions);
238
        if (options.continueRefactoring != RC_NONE)
1✔
239
            break;
1✔
240

241
        callServer(args, pipedOptions,
242
                   &editServerSubTaskFirstPass);
243
        answerEditAction();
244
    }
1✔
245
    LEAVE();
246
}
1✔
247

1✔
248
// -------------------- end of interface to edit server sub-task ----------------------
1✔
249
////////////////////////////////////////////////////////////////////////////////////////
1✔
250

1✔
251
static void displayResolutionDialog(char *message, int messageType) {
252
    char buf[TMP_BUFF_SIZE];
253
    strcpy(buf, message);
254
    formatOutputLine(buf, ERROR_MESSAGE_STARTING_OFFSET);
255
    ppcDisplaySelection(buf, messageType);
256
    beInteractive();
257
}
258

259
#define STANDARD_SELECT_SYMBOLS_MESSAGE                                                                           \
260
    "Select classes in left window. These classes will be processed during refactoring. It is highly "            \
60✔
261
    "recommended to process whole hierarchy of related classes all at once. Unselection of any class and its "    \
262
    "exclusion from refactoring may cause changes in your program behaviour."
263
#define STANDARD_C_SELECT_SYMBOLS_MESSAGE                                                                         \
60✔
264
    "There are several symbols referred from this place. Continuing this refactoring will process the selected "  \
265
    "symbols all at once."
60✔
266

×
267
static void pushReferences(EditorMarker *point, char *pushOption, char *resolveMessage,
268
                           int messageType) {
60✔
269
    /* now remake task initialisation as for edit server */
60✔
270
    parseBufferUsingServer(refactoringOptions.project, point, NULL, pushOption, NULL);
×
271

272
    assert(sessionData.browsingStack.top != NULL);
60✔
273
    if (sessionData.browsingStack.top->hkSelectedSym == NULL) {
274
        errorMessage(ERR_INTERNAL, "no symbol found for refactoring push");
11✔
275
    }
276
    createSelectionMenuForOperation(sessionData.browsingStack.top->operation);
11✔
277
    if (resolveMessage != NULL && olcxShowSelectionMenu()) {
11✔
278
        displayResolutionDialog(resolveMessage, messageType);
279
    }
11✔
280
}
×
281

282
static void safetyCheck(char *project, EditorMarker *point) {
11✔
283
    // !!!!update references MUST be followed by a pushing action, to refresh options
11✔
284
    ensureReferencesAreUpdated(refactoringOptions.project);
285
    parseBufferUsingServer(project, point, NULL, "-olcxsafetycheck", NULL);
149✔
286

287
    assert(sessionData.browsingStack.top != NULL);
288
    if (sessionData.browsingStack.top->hkSelectedSym == NULL) {
289
        errorMessage(ERR_ST, "No symbol found for refactoring safety check");
290
    }
149✔
291
    createSelectionMenuForOperation(sessionData.browsingStack.top->operation);
149✔
292
}
149✔
293

149✔
294
static char *getIdentifierOnMarker_static(EditorMarker *marker) {
295
    EditorBuffer *buffer;
380✔
296
    char         *start, *end, *textMax, *textMin;
297
    static char   identifier[TMP_STRING_SIZE];
298

299
    buffer = marker->buffer;
1,546✔
300
    assert(buffer && buffer->allocation.text && marker->offset <= buffer->allocation.bufferSize);
301
    start    = buffer->allocation.text + marker->offset;
149✔
302
    textMin = buffer->allocation.text;
149✔
303
    textMax = buffer->allocation.text + buffer->allocation.bufferSize;
149✔
304
    // move to the beginning of identifier
305
    for (; start >= textMin && (isalpha(*start) || isdigit(*start) || *start == '_' || *start == '$'); start--)
149✔
306
        ;
307
    for (start++; start < textMax && isdigit(*start); start++)
308
        ;
2✔
309
    // now get it
310
    for (end = start; end < textMax && (isalpha(*end) || isdigit(*end) || *end == '_' || *end == '$'); end++)
311
        ;
312
    int length = end - start;
313
    assert(length < TMP_STRING_SIZE - 1);
2✔
314
    strncpy(identifier, start, length);
2✔
315
    identifier[length] = 0;
2✔
316

2✔
317
    return identifier;
318
}
319

14✔
320
static char *getStringInInclude_static(EditorMarker *marker) {
321
    EditorBuffer *buffer;
322
    char         *start, *end, *textMax, *textMin;
323
    static char   string[TMP_STRING_SIZE];
18✔
324

325
    buffer = marker->buffer;
326
    assert(buffer && buffer->allocation.text && marker->offset <= buffer->allocation.bufferSize);
18✔
327
    start   = buffer->allocation.text + marker->offset;
328
    textMin = buffer->allocation.text;
2✔
329
    textMax = buffer->allocation.text + buffer->allocation.bufferSize;
330

2✔
331
    // Move to the beginning of #include
2✔
332
    for (; start >= textMin && *start != '#'; start--)
2✔
333
        ;
334
    // TODO ensure we are on an '#include'?
2✔
335
    // Move to first quote or angle bracket
336
    for (start++; start < textMax && *start != '"' && *start != '<'; start++)
337
        ;
61✔
338
    // now get it
61✔
339
    for (end = start+1; end < textMax && *end != '"' && *end != '>'; end++)
61✔
340
        ;
61✔
341
    end++;                      /* Include the terminating character */
342

42✔
343
    int length = end - start;
42✔
344
    assert(length < TMP_STRING_SIZE - 1);
42✔
345
    strncpy(string, start, length);
42✔
346
    string[length] = 0;
347

348
    return string;
×
349
}
×
350

×
351
static void replaceString(EditorMarker *marker, int len, char *newString) {
×
352
    replaceStringInEditorBuffer(marker->buffer, marker->offset, len, newString,
×
353
                                strlen(newString), &editorUndo);
×
354
}
×
355

356
static void checkedReplaceString(EditorMarker *marker, int len, char *oldString, char *newString) {
42✔
357
    char *bVal  = marker->buffer->allocation.text + marker->offset;
358
    bool check = (strlen(oldString) == len && strncmp(oldString, bVal, len) == 0);
359
    if (check) {
360
        replaceString(marker, len, newString);
130✔
361
    } else {
130✔
362
        char tmpBuff[TMP_BUFF_SIZE];
109✔
363
        sprintf(tmpBuff, "checked replacement of '%s' to '%s' failed on '", oldString, newString);
105✔
364
        int d = strlen(tmpBuff);
105✔
365
        for (int i = 0; i < len; i++)
105✔
366
            tmpBuff[d++] = bVal[i];
1✔
367
        tmpBuff[d++] = '\'';
1✔
368
        tmpBuff[d++] = 0;
1✔
369
        errorMessage(ERR_INTERNAL, tmpBuff);
3✔
370
    }
3✔
371
}
×
372

×
373
// -------------------------- Undos
374

375
static void editorFreeSingleUndo(EditorUndo *uu) {
130✔
376
    if (uu->u.replace.str != NULL && uu->u.replace.strlen != 0) {
130✔
377
        switch (uu->operation) {
378
        case UNDO_REPLACE_STRING:
54✔
379
            free(uu->u.replace.str);
380
            break;
381
        case UNDO_RENAME_BUFFER:
54✔
382
            free(uu->u.rename.name);
184✔
383
            break;
130✔
384
        case UNDO_MOVE_BLOCK:
124✔
385
            break;
124✔
386
        default:
62✔
387
            errorMessage(ERR_INTERNAL, "Unknown operation to undo");
62✔
388
        }
389
    }
390
    free(uu);
124✔
391
}
124✔
392

393
static void editorApplyUndos(EditorUndo *undos, EditorUndo *until, EditorUndo **undoundo, int gen) {
124✔
394
    EditorUndo   *uu, *next;
2✔
395
    EditorMarker *m1, *m2;
2✔
396
    uu = undos;
1✔
397
    while (uu != until && uu != NULL) {
1✔
398
        switch (uu->operation) {
399
        case UNDO_REPLACE_STRING:
2✔
400
            if (gen == GEN_FULL_OUTPUT) {
2✔
401
                ppcReplace(uu->buffer->fileName, uu->u.replace.offset,
4✔
402
                           uu->buffer->allocation.text + uu->u.replace.offset, uu->u.replace.size,
4✔
403
                           uu->u.replace.str);
4✔
404
            }
4✔
405
            replaceStringInEditorBuffer(uu->buffer, uu->u.replace.offset, uu->u.replace.size,
2✔
406
                                        uu->u.replace.str, uu->u.replace.strlen, undoundo);
2✔
407

408
            break;
4✔
409
        case UNDO_RENAME_BUFFER:
4✔
410
            if (gen == GEN_FULL_OUTPUT) {
2✔
411
                ppcGotoOffsetPosition(uu->buffer->fileName, 0);
2✔
412
                ppcGenRecord(PPC_MOVE_FILE_AS, uu->u.rename.name);
413
            }
4✔
414
            renameEditorBuffer(uu->buffer, uu->u.rename.name, undoundo);
4✔
415
            break;
4✔
416
        case UNDO_MOVE_BLOCK:
×
417
            m1 = newEditorMarker(uu->buffer, uu->u.moveBlock.offset);
×
418
            m2 = newEditorMarker(uu->u.moveBlock.dbuffer, uu->u.moveBlock.doffset);
419
            if (gen == GEN_FULL_OUTPUT) {
130✔
420
                ppcGotoMarker(m1);
130✔
421
                ppcValueRecord(PPC_REFACTORING_CUT_BLOCK, uu->u.moveBlock.size, "");
130✔
422
            }
423
            moveBlockInEditorBuffer(m1, m2, uu->u.moveBlock.size, undoundo);
54✔
424
            if (gen == GEN_FULL_OUTPUT) {
425
                ppcGotoMarker(m1);
27✔
426
                ppcGenRecord(PPC_REFACTORING_PASTE_BLOCK, "");
27✔
427
            }
27✔
428
            freeEditorMarker(m2);
27✔
429
            freeEditorMarker(m1);
430
            break;
15✔
431
        default:
432
            errorMessage(ERR_INTERNAL, "Unknown operation to undo");
15✔
433
        }
15✔
434
        next = uu->next;
15✔
435
        editorFreeSingleUndo(uu);
15✔
436
        uu = next;
437
    }
×
438
    assert(uu == until);
439
}
×
440

×
441
static void editorUndoUntil(EditorUndo *until, EditorUndo **undoundo) {
×
442
    editorApplyUndos(editorUndo, until, undoundo, GEN_NO_OUTPUT);
×
443
    editorUndo = until;
444
}
×
445

446
static void applyWholeRefactoringFromUndo(void) {
447
    EditorUndo *redoTrack;
448
    redoTrack = NULL;
449
    editorUndoUntil(refactoringStartingPoint, &redoTrack);
×
450
    editorApplyUndos(redoTrack, NULL, NULL, GEN_FULL_OUTPUT);
×
451
}
×
452

×
453
static void fatalErrorOnPosition(EditorMarker *p, int errType, char *message) {
×
454
    EditorUndo *redo;
×
455
    redo = NULL;
×
456
    editorUndoUntil(refactoringStartingPoint, &redo);
457
    ppcGotoMarker(p);
458
    FATAL_ERROR(errType, message, XREF_EXIT_ERR);
×
459
    // unreachable, but do the things properly
×
460
    editorApplyUndos(redo, NULL, &editorUndo, GEN_NO_OUTPUT);
×
461
}
×
462

463
// -------------------------- end of Undos
×
464

×
465
static void removeNonCommentCode(EditorMarker *marker, int length) {
×
466
    assert(marker->buffer && marker->buffer->allocation.text);
×
467
    char *s  = marker->buffer->allocation.text + marker->offset;
×
468
    int l = length;
×
469
    EditorMarker *mm = newEditorMarker(marker->buffer, marker->offset);
×
470
    if (marker->offset + l > marker->buffer->allocation.bufferSize) {
471
        l = marker->buffer->allocation.bufferSize - marker->offset;
×
472
    }
×
473

×
474
    int n = 0;
×
475
    while (l > 0) {
×
476
        int c = *s;
477
        if (c == '/' && l > 1 && *(s + 1) == '*' && (l <= 2 || *(s + 2) != '&')) {
×
478
            // /**/ comment
×
479
            replaceString(mm, n, "");
×
480
            s = mm->buffer->allocation.text + mm->offset;
×
481
            s += 2;
×
482
            l -= 2;
×
483
            while (!(*s == '*' && *(s + 1) == '/')) {
×
484
                s++;
485
                l--;
×
486
            }
×
487
            s += 2;
×
488
            l -= 2;
×
489
            mm->offset = s - mm->buffer->allocation.text;
×
490
            n          = 0;
491
        } else if (c == '/' && l > 1 && *(s + 1) == '/' && (l <= 2 || *(s + 2) != '&')) {
×
492
            // // comment
×
493
            replaceString(mm, n, "");
×
494
            s = mm->buffer->allocation.text + mm->offset;
×
495
            s += 2;
×
496
            l -= 2;
×
497
            while (*s != '\n') {
×
498
                s++;
×
499
                l--;
×
500
            }
×
501
            s += 1;
×
502
            l -= 1;
×
503
            mm->offset = s - mm->buffer->allocation.text;
×
504
            n          = 0;
×
505
        } else if (c == '"') {
506
            // string, pass it removing all inside (also /**/ comments)
507
            s++;
508
            l--;
×
509
            n++;
×
510
            while (*s != '"' && l > 0) {
×
511
                s++;
512
                l--;
513
                n++;
×
514
                if (*s == '\\') {
×
515
                    s++;
516
                    l--;
×
517
                    n++;
518
                    s++;
519
                    l--;
40✔
520
                    n++;
521
                }
522
            }
40✔
523
        } else {
40✔
524
            s++;
40✔
525
            l--;
40✔
526
            n++;
527
        }
74✔
528
    }
529
    if (n > 0) {
74✔
530
        replaceString(mm, n, "");
74✔
531
    }
53✔
532
    freeEditorMarker(mm);
533
}
74✔
534

535
static void renameFromTo(EditorMarker *pos, char *oldName, char *newName) {
536
    char *actName;
37✔
537
    int   nlen;
37✔
538
    nlen    = strlen(oldName);
539
    actName = getIdentifierOnMarker_static(pos);
540
    assert(strcmp(actName, oldName) == 0);
37✔
541
    checkedReplaceString(pos, nlen, oldName, newName);
37✔
542
}
543

544
static EditorMarker *createMarkerAt(EditorBuffer *buf, int offset) {
2✔
545
    EditorMarker *point;
546
    point = NULL;
547
    if (offset >= 0) {
2✔
548
        point = newEditorMarker(buf, offset);
4✔
549
    }
2✔
550
    return point;
3✔
551
}
1✔
552

553
static EditorMarker *getPointFromOptions(EditorBuffer *buf) {
554
    assert(buf);
555
    return createMarkerAt(buf, refactoringOptions.olCursorOffset);
2✔
556
}
2✔
557

2✔
558
static EditorMarker *getMarkFromOptions(EditorBuffer *buf) {
559
    assert(buf);
560
    return createMarkerAt(buf, refactoringOptions.olMarkOffset);
561
}
1✔
562

1✔
563
static void pushMarkersAsReferences(EditorMarkerList **markers, SessionStackEntry *refs, char *name) {
1✔
564
    Reference *rr;
565

566
    rr = convertEditorMarkersToReferences(markers);
567
    for (BrowserMenu *mm = refs->menu; mm != NULL; mm = mm->next) {
11✔
568
        if (strcmp(mm->referenceable.linkName, name) == 0) {
569
            for (Reference *r = rr; r != NULL; r = r->next) {
11✔
570
                addReferenceToList(r, &mm->referenceable.references);
2✔
571
            }
1✔
572
        }
1✔
573
    }
1✔
574
    freeReferences(rr);
575
    recomputeSelectedReferenceable(refs);
1✔
576
}
1✔
577

578
// ------------------------- Trivial prechecks --------------------------------------
1✔
579

1✔
580
static void askForReallyContinueConfirmation(void) {
1✔
581
    ppcAskConfirmation("The refactoring may change program behaviour, really continue?");
1✔
582
}
1✔
583

1✔
584
// ---------------------------------------------------------------------------------
585

×
586
static bool handleSafetyCheckDifferenceLists(EditorMarkerList *diff1, EditorMarkerList *diff2,
587
                                             SessionStackEntry *diffrefs) {
588
    if (diff1 != NULL || diff2 != NULL) {
589
        for (BrowserMenu *mm = diffrefs->menu; mm != NULL; mm = mm->next) {
590
            mm->selected = true;
1✔
591
            mm->visible  = true;
592
            mm->filterLevel   = 07777777;
593
            // hack, freeing now all diffs computed by old method
1✔
594
            freeReferences(mm->referenceable.references);
595
            mm->referenceable.references = NULL;
10✔
596
        }
597
        pushMarkersAsReferences(&diff1, diffrefs, LINK_NAME_SAFETY_CHECK_MISSED);
598
        pushMarkersAsReferences(&diff2, diffrefs, LINK_NAME_SAFETY_CHECK_MISSED);
11✔
599
        freeEditorMarkerListButNotMarkers(diff1);
600
        freeEditorMarkerListButNotMarkers(diff2);
601
        popFromSession();
602
        if (refactoringOptions.theRefactoring == AVR_RENAME_MODULE) {
603
            /* TODO: Handle whatever this for C! Does it even happen!?!? */
604
            displayResolutionDialog("The module already exists and is referenced in the original"
605
                                    "project. Renaming will join two modules without possibility"
606
                                    "of inverse refactoring",
607
                                    PPCV_BROWSER_TYPE_WARNING);
608
        } else {
609
            displayResolutionDialog("These references may be misinterpreted after refactoring",
610
                                    PPCV_BROWSER_TYPE_WARNING);
11✔
611
        }
612
        return false;
613
    } else
614
        return true;
615
}
616

617
static bool makeSafetyCheckAndUndo(EditorMarker *point, EditorMarkerList **occs, EditorUndo *startPoint,
11✔
618
                                   EditorUndo **redoTrack) {
11✔
619
    bool              result;
620
    EditorMarkerList *chks;
11✔
621
    EditorMarker     *defin;
622
    EditorMarkerList *diff1, *diff2;
11✔
623
    SessionStackEntry   *refs, *origrefs, *newrefs, *diffrefs;
624
    int               pbflag;
11✔
625
    UNUSED            pbflag;
626

11✔
627
    // safety check
628

11✔
629
    defin = point;
11✔
630
    // find definition reference? why this was there?
11✔
631
    //&for(dd= *occs; dd!=NULL; dd=dd->next) {
632
    //& if (isDefinitionUsage(dd->usg.base)) break;
633
    //&}
11✔
634
    //&if (dd != NULL) defin = dd->d;
635

636
    olcxPushSpecialCheckMenuSym(LINK_NAME_SAFETY_CHECK_MISSED);
637
    safetyCheck(refactoringOptions.project, defin);
638

51✔
639
    chks = convertReferencesToEditorMarkers(sessionData.browsingStack.top->references);
40✔
640

641
    editorMarkersDifferences(occs, &chks, &diff1, &diff2);
40✔
642

40✔
643
    freeEditorMarkerListAndMarkers(chks);
644

×
645
    editorUndoUntil(startPoint, redoTrack);
×
646

×
647
    origrefs = newrefs = diffrefs = NULL;
×
648
    SAFETY_CHECK_GET_SYM_LISTS(refs, origrefs, newrefs, diffrefs, pbflag);
649
    assert(origrefs != NULL && newrefs != NULL && diffrefs != NULL);
650
    result = handleSafetyCheckDifferenceLists(diff1, diff2, diffrefs);
40✔
651
    return result;
40✔
652
}
40✔
653

×
654
static void precheckThatSymbolRefsCorresponds(char *oldName, EditorMarkerList *occs) {
40✔
655
    char         *cid;
×
656
    int           off1, off2;
40✔
657
    EditorMarker *pos, *pp;
40✔
658

40✔
659
    for (EditorMarkerList *ll = occs; ll != NULL; ll = ll->next) {
660
        pos = ll->marker;
661
        // first check that I have updated reference
662
        cid = getIdentifierOnMarker_static(pos);
×
663
        if (strcmp(cid, oldName) != 0) {
664
            char tmpBuff[TMP_BUFF_SIZE];
×
665
            sprintf(tmpBuff, "something goes wrong: expecting %s instead of %s at %s, offset:%d", oldName, cid,
×
666
                    simpleFileName(getRealFileName_static(pos->buffer->fileName)), pos->offset);
×
667
            errorMessage(ERR_INTERNAL, tmpBuff);
×
668
            return;
×
669
        }
×
670
        // O.K. check also few characters around
671
        off1 = pos->offset - RRF_CHARS_TO_PRE_CHECK_AROUND;
×
672
        off2 = pos->offset + strlen(oldName) + RRF_CHARS_TO_PRE_CHECK_AROUND;
673
        if (off1 < 0)
×
674
            off1 = 0;
×
675
        if (off2 >= pos->buffer->allocation.bufferSize)
×
676
            off2 = pos->buffer->allocation.bufferSize - 1;
677
        pp = newEditorMarker(pos->buffer, off1);
678
        ppcPreCheck(pp, off2 - off1);
679
        freeEditorMarker(pp);
×
680
    }
681
}
×
682

683
static EditorMarker *createMarkerForExpressionStart(EditorMarker *marker, ExpressionStartKind startKind) {
×
684
    Position position;
×
685
    parseBufferUsingServer(refactoringOptions.project, marker, NULL, "-olcxprimarystart", NULL);
×
686
    deleteEntryFromSessionStack(sessionData.browsingStack.top);
×
687
    if (startKind == GET_PRIMARY_START) {
×
688
        position = primaryStartPosition;
×
689
    } else if (startKind == GET_STATIC_PREFIX_START) {
690
        position = staticPrefixStartPosition;
691
    } else {
692
        assert(0);
1✔
693
    }
1✔
694
    if (position.file == NO_FILE_NUMBER) {
695
        if (startKind == GET_STATIC_PREFIX_START) {
×
696
            fatalErrorOnPosition(marker, ERR_ST,
×
697
                                 "Can't determine static prefix. Maybe non-static reference to a static object? "
698
                                 "Make this invocation static before refactoring.");
1✔
699
        } else {
1✔
700
            fatalErrorOnPosition(marker, ERR_INTERNAL, "Can't determine beginning of primary expression");
701
        }
8✔
702
        return NULL;
703
    } else {
8✔
704
        EditorBuffer *buffer    = getOpenedAndLoadedEditorBuffer(getFileItemWithFileNumber(position.file)->name);
8✔
705
        EditorMarker *newMarker = newEditorMarker(buffer, 0);
4✔
706
        moveEditorMarkerToLineAndColumn(newMarker, position.line, position.col);
707
        assert(newMarker->buffer == marker->buffer);
2✔
708
        assert(newMarker->offset <= marker->offset);
2✔
709
        return newMarker;
2✔
710
    }
711
}
2✔
712

2✔
713
static void checkedRenameBuffer(EditorBuffer *buffer, char *newName, EditorUndo **undo) {
2✔
714
    if (editorFileExists(newName)) {
2✔
715
        char tmpBuff[TMP_BUFF_SIZE];
2✔
716
        sprintf(tmpBuff, "Renaming buffer %s to an existing file.\nCan I do this?", buffer->fileName);
717
        ppcAskConfirmation(tmpBuff);
718
    }
1✔
719
    renameEditorBuffer(buffer, newName, undo);
1✔
720
}
1✔
721

722
static void moveMarkerOverSpaces(EditorBuffer *buffer, EditorMarker *marker) {
1✔
723
    do {
724
        marker->offset++;
725
    } while (isspace(buffer->allocation.text[marker->offset]));
726
}
727

1✔
728
static EditorMarker *adjustMarkerForInclude(EditorMarker *marker) {
1✔
729
    EditorBuffer *buffer    = getOpenedAndLoadedEditorBuffer(marker->buffer->fileName);
1✔
730
    EditorMarker *newMarker = newEditorMarker(buffer, marker->offset);
731

1✔
732
    assert(buffer->allocation.text[newMarker->offset] == '#');
4✔
733
    moveMarkerOverSpaces(buffer, newMarker);
734
    assert(strncmp("include", &buffer->allocation.text[newMarker->offset], strlen("include")) == 0);
735
    newMarker->offset += strlen("include");
736
    moveMarkerOverSpaces(buffer, newMarker);
737
    newMarker->offset++;
3✔
738
    return newMarker;
1✔
739
}
740

2✔
741
static void renameFile(EditorMarker *marker, char *newName) {
2✔
742
    checkedRenameBuffer(marker->buffer, newName, &editorUndo);
2✔
743
}
744

745
static void renameIncludes(EditorMarkerList *markers, char *currentIncludeFileName) {
2✔
746
    char newName[MAX_FILE_NAME_SIZE];
747
    char newPath[MAX_FILE_NAME_SIZE];
748
    char currentIncludeFilePath[MAX_FILE_NAME_SIZE];
1✔
749

1✔
750
    strcpy(newName, refactoringOptions.renameTo);
751
    strcpy(newPath, normalizeFileName_static(newName, cwd));
11✔
752
    strcpy(currentIncludeFilePath, normalizeFileName_static(currentIncludeFileName, cwd));
753

754
    EditorMarker *markerForTheFile = NULL;
755
    for (EditorMarkerList *l = markers; l != NULL; l = l->next) {
51✔
756
        // References for #include always points to the '#' but there is also one that
40✔
757
        // points to the first position in the include file. We need to adjust the
758
        // markers so they point to the filename, and ignore the marker that points to
11✔
759
        // the include file.
11✔
760
        if (strcmp(l->marker->buffer->fileName, currentIncludeFilePath) == 0) {
761
            markerForTheFile = l->marker;
39✔
762
        } else {
763
            EditorMarker *adjustedMarker = adjustMarkerForInclude(l->marker);
764
            if (adjustedMarker != NULL) {
39✔
765
                checkedReplaceString(adjustedMarker, strlen(currentIncludeFileName), currentIncludeFileName,
39✔
766
                                     newName);
39✔
767
            }
768
            freeEditorMarker(adjustedMarker);
769
        }
11✔
770
    }
771
    renameFile(markerForTheFile, newName);
772
}
11✔
773

11✔
774
static void simpleRename(EditorMarkerList *markerList, EditorMarker *marker, char *symbolName,
11✔
775
                         char *symbolLinkName
776
) {
777
    // assert(options.theRefactoring == refactoringOptions.theRefactoring);
1✔
778
    assert(refactoringOptions.theRefactoring != AVR_RENAME_INCLUDED_FILE);
779
    for (EditorMarkerList *l = markerList; l != NULL; l = l->next) {
1✔
780
        renameFromTo(l->marker, symbolName, refactoringOptions.renameTo);
1✔
781
    }
782
    ppcGotoMarker(marker);
1✔
783
}
1✔
784

1✔
785
static EditorMarkerList *getReferences(EditorMarker *point, char *resolveMessage,
786
                                       int messageType) {
65✔
787
    EditorMarkerList *occs;
788
    pushReferences(point, "-olcxrename", resolveMessage, messageType); /* TODO: WTF do we use "rename"?!? */
789
    assert(sessionData.browsingStack.top && sessionData.browsingStack.top->hkSelectedSym);
790
    occs = convertReferencesToEditorMarkers(sessionData.browsingStack.top->references);
791
    return occs;
65✔
792
}
65✔
793

65✔
794
static EditorMarkerList *pushGetAndPreCheckReferences(EditorMarker *point, char *nameOnPoint,
795
                                                      char *resolveMessage, int messageType) {
65✔
796
    EditorMarkerList *occs;
65✔
797
    occs = getReferences(point, resolveMessage, messageType);
52✔
798
    precheckThatSymbolRefsCorresponds(nameOnPoint, occs);
184✔
799
    return occs;
132✔
800
}
1✔
801

802
static void multipleReferencesInSamePlaceMessage(Reference *r) {
803
    char tmpBuff[TMP_BUFF_SIZE];
804
    ppcGotoPosition(r->position);
65✔
805
    sprintf(tmpBuff, "The reference at this place refers to multiple symbols. The refactoring will probably "
806
                     "damage your program. Do you really want to continue?");
13✔
807
    formatOutputLine(tmpBuff, ERROR_MESSAGE_STARTING_OFFSET);
808
    ppcAskConfirmation(tmpBuff);
809
}
13✔
810

13✔
811
static void checkForMultipleReferencesInSamePlace(SessionStackEntry *rstack, BrowserMenu *ccms) {
13✔
812
    ReferenceableItem *p, *sss;
813
    BrowserMenu    *cms;
814
    bool            pushed;
815

11✔
816
    p = &ccms->referenceable;
11✔
817
    assert(rstack && rstack->menu);
×
818
    sss    = &rstack->menu->referenceable;
819
    pushed = itIsSymbolToPushOlReferences(p, rstack, &cms, DEFAULT_VALUE);
820
    // TODO, this can be simplified, as ccms == cms.
11✔
821
    log_debug(":checking %s to %s (%d)", p->linkName, sss->linkName, pushed);
822
    if (!pushed && haveSameBareName(p, sss)) {
11✔
823
        log_debug("checking %s references", p->linkName);
824
        for (Reference *r = p->references; r != NULL; r = r->next) {
825
            if (isReferenceInList(r, rstack->references)) {
826
                multipleReferencesInSamePlaceMessage(r);
11✔
827
            }
11✔
828
        }
829
    }
11✔
830
}
11✔
831

832
static void multipleOccurrenciesSafetyCheck(void) {
11✔
833
    SessionStackEntry *rstack;
834

11✔
835
    rstack = sessionData.browsingStack.top;
836
    processSelectedReferences(rstack, checkForMultipleReferencesInSamePlace);
11✔
837
}
838

11✔
839
// -------------------------------------------- Rename
11✔
840

1✔
841
static void renameAtPoint(EditorMarker *point) {
842
    assert(refactoringOptions.theRefactoring == AVR_RENAME_SYMBOL);
843
    if (refactoringOptions.renameTo == NULL) {
11✔
844
        errorMessage(ERR_ST, "this refactoring requires -renameto=<new name> option");
845
    }
11✔
846

847
    ensureReferencesAreUpdated(refactoringOptions.project);
11✔
848

11✔
849
    char *message = STANDARD_C_SELECT_SYMBOLS_MESSAGE;
850

2✔
851
    char nameOnPoint[TMP_STRING_SIZE];
852
    EditorMarkerList *occurrences;
853
    strcpy(nameOnPoint, getIdentifierOnMarker_static(point));
2✔
854
    assert(strlen(nameOnPoint) < TMP_STRING_SIZE - 1);
×
855
    occurrences = pushGetAndPreCheckReferences(point, nameOnPoint, message, PPCV_BROWSER_TYPE_INFO);
856

857
    BrowserMenu *symbolsMenu = sessionData.browsingStack.top->hkSelectedSym;
2✔
858
    char *symLinkName = symbolsMenu->referenceable.linkName;
859

2✔
860
    EditorUndo *undoStartPoint = editorUndo;
861

862
    multipleOccurrenciesSafetyCheck();
863

2✔
864
    simpleRename(occurrences, point, nameOnPoint, symLinkName);
2✔
865
    //&dumpEditorBuffers();
866
    EditorUndo *redoTrack = NULL;
2✔
867
    if (!makeSafetyCheckAndUndo(point, &occurrences, undoStartPoint, &redoTrack)) {
868
        askForReallyContinueConfirmation();
2✔
869
    }
870

2✔
871
    editorApplyUndos(redoTrack, NULL, NULL, GEN_FULL_OUTPUT);
1✔
872

1✔
873
    ppcGotoMarker(point);
874

875
    freeEditorMarkerListAndMarkers(occurrences); // O(n^2)!
1✔
876
}
1✔
877

878
static void renameAtInclude(EditorMarker *point) {
1✔
879
    assert(refactoringOptions.theRefactoring == AVR_RENAME_INCLUDED_FILE
880
           || refactoringOptions.theRefactoring == AVR_RENAME_MODULE);
1✔
881

1✔
882
    if (refactoringOptions.renameTo == NULL) {
1✔
883
        errorMessage(ERR_ST, "this refactoring requires -renameto=<new name> option");
884
        return;
1✔
885
    }
886

1✔
887
    ensureReferencesAreUpdated(refactoringOptions.project);
888

889
    char *message = STANDARD_C_SELECT_SYMBOLS_MESSAGE;
37✔
890

37✔
891
    char stringInInclude[TMP_STRING_SIZE];
37✔
892
    EditorMarkerList *occurrences;
37✔
893
    occurrences = getReferences(point, message, PPCV_BROWSER_TYPE_INFO);
37✔
894
    strcpy(stringInInclude, getStringInInclude_static(point));
895

9✔
896
    EditorUndo *undoStartPoint = editorUndo;
897

898
    multipleOccurrenciesSafetyCheck();
899

9✔
900
    if (stringInInclude[0] != '"') {
9✔
901
        errorMessage(ERR_ST, "You cannot rename files from the standard library");
9✔
902
        return;
9✔
903
    }
9✔
904

9✔
905
    char *includedFileName = &stringInInclude[1];
8✔
906
    stringInInclude[strlen(stringInInclude)-1] = '\0';
907

1✔
908
    renameIncludes(occurrences, includedFileName);
909

910
    EditorUndo *redoTrack = NULL;
911
    editorUndoUntil(undoStartPoint, &redoTrack);
28✔
912
    editorApplyUndos(redoTrack, NULL, NULL, GEN_FULL_OUTPUT);
913

914
    ppcGotoMarker(point);
915

28✔
916
    freeEditorMarkerListAndMarkers(occurrences); // O(n^2)!
28✔
917
}
918

×
919
static void clearParamPositions(void) {
×
920
    parameterPosition      = noPosition;
921
    parameterBeginPosition = noPosition;
×
922
    parameterEndPosition   = noPosition;
×
923
}
924

925
static Result getParameterNamePosition(EditorMarker *point, char *fileName, int argn) {
28✔
926
    char  pushOptions[TMP_STRING_SIZE];
28✔
927
    char *nameOnPoint;
28✔
928

28✔
929
    nameOnPoint = getIdentifierOnMarker_static(point);
930
    clearParamPositions();
28✔
931
    assert(strcmp(nameOnPoint, fileName) == 0);
28✔
932
    sprintf(pushOptions, "-olcxgotoparname%d", argn);
28✔
933
    parseBufferUsingServer(refactoringOptions.project, point, NULL, pushOptions, NULL);
×
934
    popFromSession();
×
935
    if (parameterPosition.file != NO_FILE_NUMBER) {
×
936
        return RESULT_OK;
937
    } else {
938
        return RESULT_ERR;
28✔
939
    }
28✔
940
}
28✔
941

28✔
942
static Result getParameterPosition(EditorMarker *point, char *functionOrMacroName, int argn) {
943
    char  pushOptions[TMP_STRING_SIZE];
×
944
    char *nameOnPoint;
×
945

×
946
    nameOnPoint = getIdentifierOnMarker_static(point);
×
947
    if (strcmp(nameOnPoint, functionOrMacroName) != 0) {
×
948
        char tmpBuff[TMP_BUFF_SIZE];
949
        ppcGotoMarker(point);
950
        sprintf(tmpBuff, "This reference is not pointing to the function/method name. Maybe a composed symbol. "
28✔
951
                         "Sorry, do not know how to handle this case.");
952
        formatOutputLine(tmpBuff, ERROR_MESSAGE_STARTING_OFFSET);
953
        errorMessage(ERR_ST, tmpBuff);
954
    }
15✔
955

956
    clearParamPositions();
957
    sprintf(pushOptions, "-olcxgetparamcoord%d", argn);
958
    parseBufferUsingServer(refactoringOptions.project, point, NULL, pushOptions, NULL);
959
    popFromSession();
960

961
    Result result = RESULT_OK;
962
    if (parameterBeginPosition.file == NO_FILE_NUMBER || parameterEndPosition.file == NO_FILE_NUMBER ||
15✔
963
        parameterBeginPosition.file == -1 || parameterEndPosition.file == -1) {
15✔
964
        ppcGotoMarker(point);
15✔
965
        errorMessage(ERR_INTERNAL, "Can't get end of parameter");
×
966
        result = RESULT_ERR;
×
967
    }
968
    // check some logical preconditions,
15✔
969
    if (parameterBeginPosition.file != parameterEndPosition.file ||
4✔
970
        parameterBeginPosition.line > parameterEndPosition.line ||
4✔
971
        (parameterBeginPosition.line == parameterEndPosition.line &&
972
         parameterBeginPosition.col > parameterEndPosition.col)) {
973
        char tmpBuff[TMP_BUFF_SIZE];
974
        ppcGotoMarker(point);
11✔
975
        sprintf(tmpBuff, "Something goes wrong at this occurence, can't get reasonable parameter limits");
976
        formatOutputLine(tmpBuff, ERROR_MESSAGE_STARTING_OFFSET);
11✔
977
        errorMessage(ERR_ST, tmpBuff);
11✔
978
        result = RESULT_ERR;
979
    }
×
980

×
981
    return result;
×
982
}
983

984
// !!!!!!!!! point and endMarker can be the same marker !!!!!!
985
static int addStringAsParameter(EditorMarker *point, EditorMarker *endMarkerOrMark, char *fileName,
11✔
986
                                int argumentNumber, char *parameterDeclaration) {
11✔
987
    char         *text;
11✔
988
    char          insertionText[REFACTORING_TMP_STRING_SIZE];
8✔
989
    char         *separator1, *separator2;
990
    int           insertionOffset;
4✔
991
    EditorMarker *beginMarker;
4✔
992

4✔
993
    insertionOffset = -1;
4✔
994
    Result rr = getParameterPosition(point, fileName, argumentNumber);
995
    if (rr != RESULT_OK) {
4✔
996
        errorMessage(ERR_INTERNAL, "Problem while adding parameter");
4✔
997
        return insertionOffset;
998
    }
999
    if (argumentNumber > parameterCount + 1) {
×
1000
        errorMessage(ERR_ST, "Parameter number out of limits");
×
1001
        return insertionOffset;
1002
    }
×
1003

×
1004

×
1005
    text = point->buffer->allocation.text;
1006

1007
    if (endMarkerOrMark == NULL) {
3✔
1008
        beginMarker = newEditorMarkerForPosition(parameterBeginPosition);
3✔
1009
    } else {
3✔
1010
        beginMarker = endMarkerOrMark;
1011
        assert(beginMarker->buffer->fileNumber == parameterBeginPosition.file);
×
1012
        moveEditorMarkerToLineAndColumn(beginMarker, parameterBeginPosition.line,
×
1013
                                        parameterBeginPosition.col);
1014
    }
3✔
1015

1016
    separator1 = "";
1017
    separator2 = "";
11✔
1018
    if (positionsAreEqual(parameterBeginPosition, parameterEndPosition)) {
1019
        if (text[beginMarker->offset] == '(') {
11✔
1020
            // function with no parameter
1021
            beginMarker->offset++;
1✔
1022
            separator1 = "";
1✔
1023
            separator2 = "";
1024
        } else if (text[beginMarker->offset] == ')') {
1025
            // beyond limit
11✔
1026
            separator1 = ", ";
1027
            separator2 = "";
11✔
1028
        } else {
11✔
1029
            char tmpBuff[TMP_BUFF_SIZE];
1030
            ppcGotoMarker(point);
11✔
1031
            sprintf(tmpBuff,
11✔
1032
                    "Something went wrong here, probably different parameter coordinates at different cpp passes.");
1033
            formatOutputLine(tmpBuff, ERROR_MESSAGE_STARTING_OFFSET);
1034
            FATAL_ERROR(ERR_INTERNAL, tmpBuff, XREF_EXIT_ERR);
11✔
1035
            assert(0);
1036
        }
1037
    } else {
8✔
1038
        if (text[beginMarker->offset] == '(') {
1039
            separator1 = "";
8✔
1040
            separator2 = ", ";
20✔
1041
        } else {
8✔
1042
            separator1 = " ";
8✔
1043
            separator2 = ",";
1044
        }
1045
        beginMarker->offset++;
8✔
1046
    }
1047

8✔
1048
    int replacementLength = 0;
1049

1050
    if (parameterListIsVoid) {
1051
        /* Then we neeed to remove the "void", or rather everything between the "()" */
1052
        replacementLength = parameterEndPosition.col - parameterBeginPosition.col - 1;
1053
        separator2        = ""; /* And there should be no separator after the new parameter */
1054
    }
1055

9✔
1056
    sprintf(insertionText, "%s%s%s", separator1, parameterDeclaration, separator2);
1057
    assert(strlen(parameterDeclaration) < REFACTORING_TMP_STRING_SIZE - 1);
1058

1059
    insertionOffset = beginMarker->offset;
9✔
1060
    replaceString(beginMarker, replacementLength, insertionText);
9✔
1061

1✔
1062
    if (endMarkerOrMark == NULL) {
1✔
1063
        freeEditorMarker(beginMarker);
1064
    }
1065

8✔
1066
    return insertionOffset;
8✔
1067
}
8✔
1068

8✔
1069
static int isThisSymbolUsed(EditorMarker *marker) {
1070
    int refn;
3✔
1071
    pushReferences(marker, "-olcxpushforlm", STANDARD_SELECT_SYMBOLS_MESSAGE, PPCV_BROWSER_TYPE_INFO);
1✔
1072
    LIST_LEN(refn, Reference, sessionData.browsingStack.top->references);
1✔
1073
    popFromSession();
2✔
1074
    return refn > 1;
2✔
1075
}
2✔
1076

1077
static int isParameterUsedExceptRecursiveCalls(EditorMarker *pmarker, EditorMarker *fmarker) {
×
1078
    // TODO: simplification for now - is it used at all?
1079
    return isThisSymbolUsed(pmarker);
1080
}
8✔
1081

1082
typedef enum {
1083
    CHECK_FOR_ADD_PARAM,
13✔
1084
    CHECK_FOR_DEL_PARAM
13✔
1085
} ParameterCheckKind;
7✔
1086

1087
static void checkThatParameterIsUnused(EditorMarker *marker, char *functionName, int argn,
5✔
1088
                                       ParameterCheckKind checkKind) {
5✔
1089
    char          parameterName[TMP_STRING_SIZE];
1090

6✔
1091
    Result result = getParameterNamePosition(marker, functionName, argn);
1092
    if (result != RESULT_OK) {
13✔
1093
        ppcAskConfirmation("Can not parse parameter definition, continue anyway?");
1094
        return;
11✔
1095
    }
1096

1097
    EditorMarker *positionMarker = newEditorMarkerForPosition(parameterPosition);
1098
    strncpy(parameterName, getIdentifierOnMarker_static(positionMarker), TMP_STRING_SIZE);
11✔
1099
    parameterName[TMP_STRING_SIZE - 1] = 0;
11✔
1100
    if (isParameterUsedExceptRecursiveCalls(positionMarker, marker)) {
×
1101
        char tmpBuff[TMP_BUFF_SIZE];
1102
        if (checkKind == CHECK_FOR_ADD_PARAM) {
11✔
1103
            sprintf(tmpBuff, "parameter '%s' clashes with an existing symbol, continue anyway?", parameterName);
11✔
1104
            ppcAskConfirmation(tmpBuff);
1105
        } else if (checkKind == CHECK_FOR_DEL_PARAM) {
11✔
1106
            sprintf(tmpBuff, "parameter '%s' is used, delete it anyway?", parameterName);
1107
            ppcAskConfirmation(tmpBuff);
11✔
1108
        } else {
3✔
1109
            assert(0);
1110
        }
×
1111
    }
1112
    freeEditorMarker(positionMarker);
1113
}
×
1114

1115
static void addParameter(EditorMarker *pos, char *fname, int argumentNumber, Usage usage) {
1116
    if (isDefinitionOrDeclarationUsage(usage)) {
×
1117
        if (addStringAsParameter(pos, NULL, fname, argumentNumber, refactoringOptions.refactor_parameter_name) != -1)
1118
            // now check that there is no conflict
3✔
1119
            if (isDefinitionUsage(usage))
1120
                checkThatParameterIsUnused(pos, fname, argumentNumber, CHECK_FOR_ADD_PARAM);
8✔
1121
    } else {
6✔
1122
        addStringAsParameter(pos, NULL, fname, argumentNumber, refactoringOptions.refactor_parameter_value);
6✔
1123
    }
2✔
1124
}
1125

1126
static void deleteParameter(EditorMarker *pos, char *fname, int argumentNumber, Usage usage) {
6✔
1127
    char         *text;
1128
    EditorMarker *m1, *m2;
8✔
1129

1130
    Result res = getParameterPosition(pos, fname, argumentNumber);
1131
    if (res != RESULT_OK)
4✔
1132
        return;
1133

1134
    m1 = newEditorMarkerForPosition(parameterBeginPosition);
8✔
1135
    m2 = newEditorMarkerForPosition(parameterEndPosition);
1136

11✔
1137
    text = pos->buffer->allocation.text;
11✔
1138

1139
    if (positionsAreEqual(parameterBeginPosition, parameterEndPosition)) {
1140
        if (text[m1->offset] == '(') {
2✔
1141
            // function with no parameter
1142
        } else if (text[m1->offset] == ')') {
1143
            // beyond limit
1144
        } else {
1145
            FATAL_ERROR(ERR_INTERNAL,
1146
                       "Something goes wrong, probably different parameter coordinates at different cpp passes.",
2✔
1147
                       XREF_EXIT_ERR);
2✔
1148
            assert(0);
×
1149
        }
1150
        errorMessage(ERR_ST, "Parameter number out of limits");
2✔
1151
    } else {
2✔
1152
        if (text[m1->offset] == '(') {
1153
            m1->offset++;
2✔
1154
            if (text[m2->offset] == ',') {
2✔
1155
                m2->offset++;
2✔
1156
                // here pass also blank symbols
1157
            }
2✔
1158
            moveEditorMarkerToNonBlank(m2, 1);
×
1159
        }
1160
        if (isDefinitionUsage(usage)) {
×
1161
            // this must be at the end, because it discards values
1162
            // of s_paramBeginPosition and s_paramEndPosition
1163
            checkThatParameterIsUnused(pos, fname, argumentNumber, CHECK_FOR_DEL_PARAM);
×
1164
        }
1165

1166
        assert(m1->offset <= m2->offset);
×
1167
        replaceString(m1, m2->offset - m1->offset, "");
1168
    }
×
1169
    freeEditorMarker(m1);
1170
    freeEditorMarker(m2);
2✔
1171
}
2✔
1172

2✔
1173
static void moveParameter(EditorMarker *pos, char *fname, int argFrom, int argTo) {
2✔
1174
    char         *text;
2✔
1175
    char          par[REFACTORING_TMP_STRING_SIZE];
2✔
1176
    int           plen;
2✔
1177
    EditorMarker *m1, *m2;
2✔
1178

2✔
1179
    Result res = getParameterPosition(pos, fname, argFrom);
2✔
1180
    if (res != RESULT_OK)
1181
        return;
2✔
1182

2✔
1183
    m1 = newEditorMarkerForPosition(parameterBeginPosition);
1184
    m2 = newEditorMarkerForPosition(parameterEndPosition);
1185

13✔
1186
    text      = pos->buffer->allocation.text;
1187
    plen      = 0;
1188
    par[plen] = 0;
1189

1190
    if (positionsAreEqual(parameterBeginPosition, parameterEndPosition)) {
37✔
1191
        if (text[m1->offset] == '(') {
13✔
1192
            // function with no parameter
37✔
1193
        } else if (text[m1->offset] == ')') {
24✔
1194
            // beyond limit
1195
        } else {
24✔
1196
            FATAL_ERROR(ERR_INTERNAL,
13✔
1197
                       "Something goes wrong, probably different parameter coordinates at different cpp passes.",
11✔
1198
                       XREF_EXIT_ERR);
9✔
1199
            assert(0);
2✔
1200
        }
2✔
1201
        errorMessage(ERR_ST, "Parameter out of limit");
1202
    } else {
×
1203
        m1->offset++;
×
1204
        moveEditorMarkerToNonBlank(m1, 1);
1205
        m2->offset--;
1206
        moveEditorMarkerToNonBlank(m2, -1);
24✔
1207
        m2->offset++;
1208
        assert(m1->offset <= m2->offset);
13✔
1209
        plen = m2->offset - m1->offset;
13✔
1210
        strncpy(par, MARKER_TO_POINTER(m1), plen);
1211
        par[plen] = 0;
1212
        deleteParameter(pos, fname, argFrom, UsageUsed);
1213
        addStringAsParameter(pos, NULL, fname, argTo, par);
13✔
1214
    }
1215
    freeEditorMarker(m1);
1216
    freeEditorMarker(m2);
1217
}
1218

13✔
1219
static void applyParameterManipulationToFunction(char *functionName, EditorMarkerList *occurrences,
1220
                                                 int manipulation, int argn1, int argn2) {
13✔
1221
    int progress, count;
13✔
1222

13✔
1223
    /* TODO Is it guaranteed that the occurrences always starts with Defined/Declared? */
1224
    LIST_LEN(count, EditorMarkerList, occurrences);
13✔
1225
    progress = 0;
1226
    for (EditorMarkerList *l = occurrences; l != NULL; l = l->next) {
13✔
1227
        if (l->usage != UsageUndefinedMacro) {
13✔
1228
            /* TODO: Should we not abort if any of the occurrences fail? */
13✔
1229
            if (manipulation == PPC_AVR_ADD_PARAMETER) {
1230
                addParameter(l->marker, functionName, argn1, l->usage);
13✔
1231
            } else if (manipulation == PPC_AVR_DEL_PARAMETER) {
13✔
1232
                deleteParameter(l->marker, functionName, argn1, l->usage);
1233
            } else if (manipulation == PPC_AVR_MOVE_PARAMETER) {
13✔
1234
                moveParameter(l->marker, functionName, argn1, argn2);
13✔
1235
            } else {
13✔
1236
                errorMessage(ERR_INTERNAL, "unknown parameter manipulation");
1237
                break;
1238
            }
1239
        }
1240
        writeRelativeProgress((100 * progress++)/count);
1241
    }
2✔
1242
    writeRelativeProgress(100);
1243
}
2✔
1244

2✔
1245
// -------------------------------------- ParameterManipulations
2✔
1246

×
1247
static void applyParameterManipulation(EditorMarker *point, int manipulation, int argn1,
1248
                                       int argn2) {
×
1249
    char              nameOnPoint[TMP_STRING_SIZE];
×
1250
    EditorMarkerList *occurrences;
×
1251

×
1252
    ensureReferencesAreUpdated(refactoringOptions.project);
×
1253

×
1254
    strcpy(nameOnPoint, getIdentifierOnMarker_static(point));
×
1255
    pushReferences(point, "-olcxargmanip", STANDARD_SELECT_SYMBOLS_MESSAGE, PPCV_BROWSER_TYPE_INFO);
×
1256
    occurrences = convertReferencesToEditorMarkers(sessionData.browsingStack.top->references);
×
1257

×
1258
    ppcGotoMarker(point);
×
1259

1260
    applyParameterManipulationToFunction(nameOnPoint, occurrences, manipulation, argn1, argn2);
×
1261
    freeEditorMarkerListAndMarkers(occurrences); // O(n^2)!
×
1262
}
×
1263

1264
static void parameterManipulation(EditorMarker *point, int manip, int argn1, int argn2) {
×
1265
    applyParameterManipulation(point, manip, argn1, argn2);
×
1266
    // and generate output
×
1267
    applyWholeRefactoringFromUndo();
×
1268
    ppcGotoMarker(point);
1269
}
×
1270

2✔
1271
//------------------------------------------------------------
2✔
1272

1273
// basically move marker to the first non blank and non comment symbol at the same
×
1274
// line as the marker is or to the newline character
×
1275
static void moveMarkerToTheEndOfDefinitionScope(EditorMarker *mm) {
×
1276
    int offset;
1277
    offset = mm->offset;
1278
    moveEditorMarkerToNonBlankOrNewline(mm, 1);
1279
    if (mm->offset >= mm->buffer->allocation.bufferSize) {
×
1280
        return;
1281
    }
×
1282
    if (CHAR_ON_MARKER(mm) == '/' && CHAR_AFTER_MARKER(mm) == '/') {
×
1283
        if (refactoringOptions.commentMovingMode == CM_NO_COMMENT)
×
1284
            return;
×
1285
        moveEditorMarkerToNewline(mm, 1);
×
1286
        mm->offset++;
×
1287
    } else if (CHAR_ON_MARKER(mm) == '/' && CHAR_AFTER_MARKER(mm) == '*') {
×
1288
        if (refactoringOptions.commentMovingMode == CM_NO_COMMENT)
1289
            return;
×
1290
        mm->offset++;
×
1291
        mm->offset++;
×
1292
        while (mm->offset < mm->buffer->allocation.bufferSize &&
×
1293
               (CHAR_ON_MARKER(mm) != '*' || CHAR_AFTER_MARKER(mm) != '/')) {
×
1294
            mm->offset++;
×
1295
        }
×
1296
        if (mm->offset < mm->buffer->allocation.bufferSize) {
×
1297
            mm->offset++;
1298
            mm->offset++;
×
1299
        }
1300
        offset = mm->offset;
×
1301
        moveEditorMarkerToNonBlankOrNewline(mm, 1);
×
1302
        if (CHAR_ON_MARKER(mm) == '\n')
×
1303
            mm->offset++;
×
1304
        else
×
1305
            mm->offset = offset;
×
1306
    } else if (CHAR_ON_MARKER(mm) == '\n') {
×
1307
        mm->offset++;
×
1308
    } else {
1309
        if (refactoringOptions.commentMovingMode == CM_NO_COMMENT)
×
1310
            return;
1311
        mm->offset = offset;
×
1312
    }
×
1313
}
×
1314

×
1315
static int markerWRTComment(EditorMarker *mm, int *commentBeginOffset) {
×
1316
    char *b, *s, *e, *mms;
×
1317
    assert(mm->buffer && mm->buffer->allocation.text);
1318
    s   = mm->buffer->allocation.text;
1319
    e   = s + mm->buffer->allocation.bufferSize;
×
1320
    mms = s + mm->offset;
×
1321
    while (s < e && s < mms) {
1322
        b = s;
×
1323
        if (*s == '/' && (s + 1) < e && *(s + 1) == '*') {
1324
            // /**/ comment
1325
            s += 2;
×
1326
            while ((s + 1) < e && !(*s == '*' && *(s + 1) == '/'))
1327
                s++;
1328
            if (s + 1 < e)
2✔
1329
                s += 2;
1330
            if (s > mms) {
1331
                *commentBeginOffset = b - mm->buffer->allocation.text;
1332
                return MARKER_IS_IN_STAR_COMMENT;
2✔
1333
            }
1334
        } else if (*s == '/' && s + 1 < e && *(s + 1) == '/') {
2✔
1335
            // // comment
2✔
1336
            s += 2;
2✔
1337
            while (s < e && *s != '\n')
2✔
1338
                s++;
1✔
1339
            if (s < e)
1✔
1340
                s += 1;
1341
            if (s > mms) {
2✔
1342
                *commentBeginOffset = b - mm->buffer->allocation.text;
2✔
1343
                return MARKER_IS_IN_SLASH_COMMENT;
×
1344
            }
×
1345
        } else if (*s == '"') {
×
1346
            // string, pass it removing all inside (also /**/ comments)
×
1347
            s++;
×
1348
            while (s < e && *s != '"') {
×
1349
                s++;
×
1350
                if (*s == '\\') {
×
1351
                    s++;
×
1352
                    s++;
×
1353
                }
×
1354
            }
×
1355
            if (s < e)
×
1356
                s++;
×
1357
        } else {
×
1358
            s++;
×
1359
        }
1360
    }
1361
    return MARKER_IS_IN_CODE;
×
1362
}
×
1363

×
1364
static void moveMarkerToTheBeginOfDefinitionScope(EditorMarker *mm) {
×
1365
    int theBeginningOffset, comBeginOffset, mp;
×
1366
    int slashedCommentsProcessed, staredCommentsProcessed;
×
1367

×
1368
    slashedCommentsProcessed = staredCommentsProcessed = 0;
×
1369
    for (;;) {
×
1370
        theBeginningOffset = mm->offset;
×
1371
        mm->offset--;
×
1372
        moveEditorMarkerToNonBlankOrNewline(mm, -1);
×
1373
        if (CHAR_ON_MARKER(mm) == '\n') {
1374
            theBeginningOffset = mm->offset + 1;
×
1375
            mm->offset--;
×
1376
        }
1377
        if (refactoringOptions.commentMovingMode == CM_NO_COMMENT)
1378
            goto fini;
2✔
1379
        moveEditorMarkerToNonBlank(mm, -1);
2✔
1380
        mp = markerWRTComment(mm, &comBeginOffset);
2✔
1381
        if (mp == MARKER_IS_IN_CODE)
1382
            goto fini;
2✔
1383
        else if (mp == MARKER_IS_IN_STAR_COMMENT) {
1384
            if (refactoringOptions.commentMovingMode == CM_SINGLE_SLASHED)
1385
                goto fini;
1386
            if (refactoringOptions.commentMovingMode == CM_ALL_SLASHED)
2✔
1387
                goto fini;
1388
            if (staredCommentsProcessed > 0 && refactoringOptions.commentMovingMode == CM_SINGLE_STARRED)
1389
                goto fini;
2✔
1390
            if (staredCommentsProcessed > 0 &&
1391
                refactoringOptions.commentMovingMode == CM_SINGLE_SLASHED_AND_STARRED)
2✔
1392
                goto fini;
×
1393
            staredCommentsProcessed++;
1394
            mm->offset = comBeginOffset;
1395
        }
2✔
1396
        // slash comment, skip them all
2✔
1397
        else if (mp == MARKER_IS_IN_SLASH_COMMENT) {
1398
            if (refactoringOptions.commentMovingMode == CM_SINGLE_STARRED)
2✔
1399
                goto fini;
2✔
1400
            if (refactoringOptions.commentMovingMode == CM_ALL_STARRED)
1401
                goto fini;
2✔
1402
            if (slashedCommentsProcessed > 0 && refactoringOptions.commentMovingMode == CM_SINGLE_SLASHED)
2✔
1403
                goto fini;
2✔
1404
            if (slashedCommentsProcessed > 0 &&
1405
                refactoringOptions.commentMovingMode == CM_SINGLE_SLASHED_AND_STARRED)
2✔
1406
                goto fini;
1407
            slashedCommentsProcessed++;
1408
            mm->offset = comBeginOffset;
1409
        } else {
1410
            warningMessage(ERR_INTERNAL, "A new comment?");
2✔
1411
            goto fini;
1412
        }
2✔
1413
    }
×
1414
fini:
2✔
1415
    mm->offset = theBeginningOffset;
2✔
1416
}
2✔
1417

2✔
1418
static void getFunctionBoundariesForMoving(EditorMarker *point, EditorMarker **methodStartP,
1419
                                           EditorMarker **methodEndP) {
1420
    EditorMarker *mstart, *mend;
2✔
1421

2✔
1422
    parsedPositions[IPP_FUNCTION_BEGIN].file = parsedPositions[IPP_FUNCTION_END].file = NO_FILE_NUMBER;
1423

2✔
1424
    // get function boundaries
2✔
1425
    parseBufferUsingServer(refactoringOptions.project, point, NULL, "-olcxgetfunctionbounds", NULL);
×
1426

×
1427
    if (parsedPositions[IPP_FUNCTION_BEGIN].file == NO_FILE_NUMBER || parsedPositions[IPP_FUNCTION_END].file == NO_FILE_NUMBER) {
1428
        FATAL_ERROR(ERR_INTERNAL, "Can't find declaration coordinates", XREF_EXIT_ERR);
2✔
1429
    }
1430

1431
    mstart = newEditorMarkerForPosition(parsedPositions[IPP_FUNCTION_BEGIN]);
×
1432
    mend   = newEditorMarkerForPosition(parsedPositions[IPP_FUNCTION_BEGIN + 1]);
1433

1434
    moveMarkerToTheBeginOfDefinitionScope(mstart);
×
1435
    moveMarkerToTheEndOfDefinitionScope(mend);
×
1436

1437
    assert(mstart->buffer == mend->buffer);
×
1438
    *methodStartP = mstart;
1439
    *methodEndP   = mend;
×
1440
}
×
1441

1442
static EditorMarker *getTargetFromOptions(void) {
×
1443
    EditorMarker *target;
1444
    EditorBuffer *tb;
×
1445
    int           tline;
1446

1447
    tb = findOrCreateAndLoadEditorBufferForFile(
2✔
1448
        normalizeFileName_static(refactoringOptions.moveTargetFile, cwd));
1449
    if (tb == NULL)
1450
        FATAL_ERROR(ERR_ST, "Could not find a buffer for target position", XREF_EXIT_ERR);
2✔
1451
    target = newEditorMarker(tb, 0);
×
1452
    sscanf(refactoringOptions.refactor_target_line, "%d", &tline);
×
1453
    moveEditorMarkerToLineAndColumn(target, tline, 0);
×
1454
    return target;
1455
}
1456

1457
static bool validTargetPlace(EditorMarker *target, char *checkOpt) {
1458
    bool valid = true;
1459

2✔
1460
    parseBufferUsingServer(refactoringOptions.project, target, NULL, checkOpt, NULL);
2✔
1461
    if (!parsedInfo.moveTargetAccepted) {
2✔
1462
        valid = false;
2✔
1463
        errorMessage(ERR_ST, "Invalid target place");
1464
    }
2✔
1465
    return valid;
1✔
1466
}
1✔
1467

1✔
1468
EditorMarker *removeStaticPrefix(EditorMarker *marker) {
1469
    EditorMarker *startMarker;
1470

1✔
1471
    startMarker = createMarkerForExpressionStart(marker, GET_STATIC_PREFIX_START);
1✔
1472
    if (startMarker == NULL) {
1✔
1473
        // this is an error, this is just to avoid possible core dump in the future
1✔
1474
        startMarker = newEditorMarker(marker->buffer, marker->offset);
1475
    } else {
×
1476
        int savedOffset = startMarker->offset;
1477
        removeNonCommentCode(startMarker, marker->offset - startMarker->offset);
1478
        // return it back to beginning of name(?)
2✔
1479
        startMarker->offset = savedOffset;
1480
    }
1481
    return startMarker;
2✔
1482
}
1✔
1483

1✔
1484
static void moveStaticFunctionAndMakeItExtern(EditorMarker *startMarker, EditorMarker *point,
1485
                                              EditorMarker *endMarker, EditorMarker *target,
1✔
1486
                                              ToCheckOrNot check, int limitIndex) {
1487
    int size = endMarker->offset - startMarker->offset;
1488
    if (target->buffer == startMarker->buffer && target->offset > startMarker->offset &&
1489
        target->offset < startMarker->offset + size) {
2✔
1490
        ppcGenRecord(PPC_INFORMATION, "You can't move something into itself.");
1491
        return;
1492
    }
2✔
1493

2✔
1494
    /* Check if function has "static" keyword and remove it when moving between files.
1495
     * When moving within the same file, keep static (visibility doesn't change).
2✔
1496
     * For Phase 1 MVP: we just remove static, user manually handles extern/headers. */
×
1497
    bool movingBetweenFiles = (startMarker->buffer != target->buffer);
1498
    EditorMarker *searchMarker = newEditorMarker(startMarker->buffer, startMarker->offset);
2✔
1499
    bool foundStatic = false;
1500
    EditorMarker *staticMarker = NULL;
1501

2✔
1502
    if (movingBetweenFiles) {
2✔
1503
        while (searchMarker->offset < point->offset) {
1504
            char *text = &startMarker->buffer->allocation.text[searchMarker->offset];
1505
            int remaining = point->offset - searchMarker->offset;
2✔
1506

1507
            /* Check if we're at "static " (with space or tab after) */
1508
            if (remaining >= 7 && strncmp(text, "static", 6) == 0 &&
2✔
1509
                (text[6] == ' ' || text[6] == '\t')) {
2✔
1510
                foundStatic = true;
2✔
1511
                staticMarker = newEditorMarker(startMarker->buffer, searchMarker->offset);
1512
                break;
1513
            }
1514
            searchMarker->offset++;
6✔
1515
        }
6✔
1516
    }
6✔
1517
    freeEditorMarker(searchMarker);
1518

2✔
1519
    /* Remove "static " keyword BEFORE moving (only when moving between files) */
2✔
1520
    if (foundStatic) {
2✔
1521
        replaceStringInEditorBuffer(staticMarker->buffer, staticMarker->offset, 7, "", 0, &editorUndo);
1522
        freeEditorMarker(staticMarker);
1✔
1523
        /* Adjust size since we removed 7 characters */
1✔
1524
        size -= 7;
1✔
1525
    }
1526

26✔
1527
    /* Now move the (possibly modified) function block */
1528
    moveBlockInEditorBuffer(startMarker, target, size, &editorUndo);
1529
}
1530

26✔
1531
static void moveFunction(EditorMarker *point) {
1532
    EditorMarker *target = getTargetFromOptions();
26✔
1533

26✔
1534
    if (!validTargetPlace(target, "-olcxmovetarget"))
26✔
1535
        return;
26✔
1536

26✔
1537
    ensureReferencesAreUpdated(refactoringOptions.project);
26✔
1538

1539
    EditorMarker *functionStart, *functionEnd;
26✔
1540
    getFunctionBoundariesForMoving(point, &functionStart, &functionEnd);
×
1541
    int lines = countLinesBetweenEditorMarkers(functionStart, functionEnd);
1542

26✔
1543
    // O.K. Now STARTING!
1544
    moveStaticFunctionAndMakeItExtern(functionStart, point, functionEnd, target, APPLY_CHECKS, IPP_FUNCTION_BEGIN);
93✔
1545

67✔
1546
    // and generate output
67✔
1547
    applyWholeRefactoringFromUndo();
4✔
1548
    ppcGotoMarker(point);
1549
    ppcValueRecord(PPC_INDENT, lines, "");
67✔
1550
}
3✔
1551
// ------------------------------------------------------ Extract
1552

1553
static void extractFunction(EditorMarker *point, EditorMarker *mark) {
1554
    parseBufferUsingServer(refactoringOptions.project, point, mark, "-olcxextract", NULL);
26✔
1555
}
1556

5✔
1557
static void extractMacro(EditorMarker *point, EditorMarker *mark) {
21✔
1558
    parseBufferUsingServer(refactoringOptions.project, point, mark, "-olcxextract", "-olexmacro");
1559
}
3✔
1560

18✔
1561
static void extractVariable(EditorMarker *point, EditorMarker *mark) {
1562
    parseBufferUsingServer(refactoringOptions.project, point, mark, "-olcxextract", "-olexvariable");
×
1563
}
×
1564

×
1565
static char *computeUpdateOptionForSymbol(EditorMarker *point) {
1566
    int               fileNumber;
×
1567
    char             *selectedUpdateOption;
1568

18✔
1569
    assert(point != NULL && point->buffer != NULL);
1570
    currentLanguage = getLanguageFor(point->buffer->fileName);
1571

1572
    bool hasHeaderReferences = false;
18✔
1573
    bool isMultiFileReferences = false;
1574
    EditorMarkerList *markerList = getReferences(point, NULL, PPCV_BROWSER_TYPE_WARNING);
1575
    BrowserMenu *menu = sessionData.browsingStack.top->hkSelectedSym;
1576
    Scope scope = menu->referenceable.scope;
×
1577
    Visibility visibility = menu->referenceable.visibility;
1578

1579
    if (markerList == NULL) {
26✔
1580
        fileNumber = NO_FILE_NUMBER;
26✔
1581
    } else {
26✔
1582
        assert(markerList->marker != NULL && markerList->marker->buffer != NULL);
1583
        fileNumber = markerList->marker->buffer->fileNumber;
26✔
1584
    }
1585
    for (EditorMarkerList *l = markerList; l != NULL; l = l->next) {
1586
        assert(l->marker != NULL && l->marker->buffer != NULL);
1587
        FileItem *fileItem = getFileItemWithFileNumber(l->marker->buffer->fileNumber);
1588
        if (fileNumber != l->marker->buffer->fileNumber) {
1589
            isMultiFileReferences = true;
1590
        }
1591
        if (!fileItem->isArgument) {
1592
            hasHeaderReferences = true;
1593
        }
1594
    }
1595

1596
    if (visibility == VisibilityLocal) {
1597
        // useless to update when there is nothing about the symbol in Tags
1598
        selectedUpdateOption = "";
1599
    } else if (hasHeaderReferences) {
1600
        // once it is in a header, full update is required
1601
        selectedUpdateOption = "-update";
1602
    } else if (scope == AutoScope || scope == FileScope) {
1603
        // for example a local var or a static function not used in any header
1604
        if (isMultiFileReferences) {
1605
            errorMessage(ERR_INTERNAL, "something went wrong, a local symbol is used in several files");
1606
            selectedUpdateOption = "-update";
1607
        } else {
1608
            selectedUpdateOption = "";
1609
        }
1610
    } else if (!isMultiFileReferences) {
37✔
1611
        // this is a little bit tricky. It may provoke a bug when
1612
        // a new external function is not yet indexed, but used in another file.
37✔
1613
        // But it is so practical, so take the risk.
1614
        selectedUpdateOption = "";
37✔
1615
    } else {
×
1616
        // may seems too strong, but implicitly linked global functions
1617
        // requires this (for example).
1618
        selectedUpdateOption = "-fastupdate";
37✔
1619
    }
1620

1621
    freeEditorMarkerListAndMarkers(markerList);
1622
    markerList = NULL;
37✔
1623
    popFromSession();
1624

1625
    return selectedUpdateOption;
1626
}
1627

37✔
1628
// --------------------------------------------------------------------
1629

37✔
1630
/* Main entry point for refactoring operations (-refactory mode).
37✔
1631
 *
1632
 * This function is called when c-xref is invoked with the -refactory option.
37✔
1633
 * It runs as a separate process from the main editor server and handles the
1634
 * complete lifecycle of a refactoring operation.
1635
 *
37✔
1636
 * PROCESS LIFECYCLE:
37✔
1637
 * 1. Performs initial refactoring operation (rename, extract, parameter manipulation, etc.)
1638
 * 2. If user interaction is needed (e.g., name collisions, symbol selection), enters
37✔
1639
 *    beInteractive() which waits for piped commands from the editor
×
1640
 * 3. When -continuerefactoring is received, exits interactive mode
1641
 * 4. Completes the refactoring and THE PROCESS EXITS AUTOMATICALLY
1642
 *
37✔
1643
 * IMPORTANT: The refactory process always exits when this function returns. Tests and
37✔
1644
 * editor clients should NOT send an explicit <exit> command after -continuerefactoring,
37✔
1645
 * as the process will have already terminated (causing a race condition in tests).
1646
 *
37✔
1647
 * The function never returns to a command loop - it's a one-shot operation that either:
37✔
1648
 * - Completes immediately (for simple refactorings)
1649
 * - Enters beInteractive() for user decisions, then completes
37✔
1650
 * - Errors out via FATAL_ERROR
1651
 */
1652
void refactory(void) {
37✔
1653

37✔
1654
    ENTER();
37✔
1655

1656
    if (options.project == NULL) {
37✔
1657
        FATAL_ERROR(ERR_ST, "You have to specify active project with -p option", XREF_EXIT_ERR);
1658
    }
37✔
1659

11✔
1660
    deepCopyOptionsFromTo(&options, &refactoringOptions); // save command line options !!!!
11✔
1661
    // in general in this file:
11✔
1662
    //   'refactoringOptions' are options passed to c-xrefactory
11✔
1663
    //   'options' are options valid for interactive edit-server 'sub-task'
11✔
1664
    deepCopyOptionsFromTo(&options, &savedOptions);
2✔
1665

1666
    // MAGIC, set the server operation to anything that just refreshes
2✔
1667
    // or generates xrefs since we will be calling the "main task"
2✔
1668
    // below
2✔
1669
    refactoringOptions.serverOperation = OLO_LIST;
2✔
1670

13✔
1671
    openOutputFile(refactoringOptions.outputFileName);
1672
    loadAllOpenedEditorBuffers();
1673
    // initialise lastQuasySaveTime
13✔
1674
    quasiSaveModifiedEditorBuffers();
13✔
1675

13✔
1676

13✔
1677
    int argCount = 0;
1678
    char *argumentFile = getNextScheduledFile(&argCount);
13✔
1679

2✔
1680
    if (argumentFile == NULL)
2✔
1681
        FATAL_ERROR(ERR_ST, "no input file", XREF_EXIT_ERR);
2✔
1682

2✔
1683
    char inputFileName[MAX_FILE_NAME_SIZE];
6✔
1684
    strcpy(inputFileName, argumentFile);
6✔
1685
    char *file = inputFileName;
6✔
1686
    EditorBuffer *buf = findOrCreateAndLoadEditorBufferForFile(file);
6✔
1687

2✔
1688
    EditorMarker *point = getPointFromOptions(buf);
2✔
1689
    EditorMarker *mark  = getMarkFromOptions(buf);
2✔
1690

2✔
1691
    refactoringStartingPoint = editorUndo;
1✔
1692

1✔
1693
    // init subtask
1✔
1694
    ArgumentsVector args = {.argc = argument_count(serverStandardOptions), .argv = serverStandardOptions};
1✔
1695
    mainTaskEntryInitialisations(args);
×
1696
    editServerSubTaskFirstPass = true;
×
1697

×
1698
    progressFactor = 1;
1699

1700
    switch (refactoringOptions.theRefactoring) {
1701
    case AVR_RENAME_SYMBOL:
37✔
1702
        progressFactor = 3;
37✔
1703
        updateOption   = computeUpdateOptionForSymbol(point);
1704
        renameAtPoint(point);
37✔
1705
        break;
1706
    case AVR_RENAME_MODULE:
×
1707
    case AVR_RENAME_INCLUDED_FILE:
×
1708
        progressFactor = 2;
1709
        updateOption   = computeUpdateOptionForSymbol(point);
1710
        renameAtInclude(point);
1711
        break;
37✔
1712
    case AVR_ADD_PARAMETER:
1713
    case AVR_DEL_PARAMETER:
37✔
1714
    case AVR_MOVE_PARAMETER:
37✔
1715
        progressFactor = 3;
1716
        updateOption   = computeUpdateOptionForSymbol(point);
1717
        currentLanguage = getLanguageFor(file);
37✔
1718
        parameterManipulation(point, refactoringOptions.theRefactoring, refactoringOptions.parnum,
1719
                              refactoringOptions.parnum2);
37✔
1720
        break;
37✔
1721
    case AVR_MOVE_FUNCTION:
1722
        progressFactor = 2;  /* Simple move without multi-step progress */
1723
        moveFunction(point);
1724
        break;
1725
    case AVR_EXTRACT_FUNCTION:
1726
        progressFactor = 1;
1727
        extractFunction(point, mark);
1728
        break;
1729
    case AVR_EXTRACT_MACRO:
1730
        progressFactor = 1;
1731
        extractMacro(point, mark);
1732
        break;
1733
    case AVR_EXTRACT_VARIABLE:
1734
        progressFactor = 1;
1735
        extractVariable(point, mark);
1736
        break;
1737
    default:
1738
        errorMessage(ERR_INTERNAL, "unknown refactoring");
1739
        break;
1740
    }
1741

1742
    // always finish once more time
1743
    writeRelativeProgress(0);
1744
    writeRelativeProgress(100);
1745

1746
    if (progressOffset != progressFactor) {
1747
        char tmpBuff[TMP_BUFF_SIZE];
1748
        sprintf(tmpBuff, "progressOffset (%d) != progressFactor (%d)", progressOffset, progressFactor);
1749
        ppcGenRecord(PPC_DEBUG_INFORMATION, tmpBuff);
1750
    }
1751

1752
    // synchronisation, wait so files won't be saved with the same time
1753
    quasiSaveModifiedEditorBuffers();
1754

1755
    closeOutputFile();
1756
    ppcSynchronize();
1757

1758
    // exiting, put undefined, so that main will finish
1759
    options.mode = UndefinedMode;
1760

1761
    LEAVE();
1762
}
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