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

thoni56 / c-xrefactory / 1795

17 May 2026 09:16PM UTC coverage: 85.242% (+0.1%) from 85.141%
1795

push

travis-ci

thoni56
[requests][tidy] Change request -olcxnext/previous to -browse-next/previous

16381 of 19217 relevant lines covered (85.24%)

15918297.46 hits per line

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

87.17
src/editor.c
1
#include "editor.h"
2

3
#include <stdlib.h>
4
#include <memory.h>
5

6
#include "commons.h"
7
#include "editorbuffer.h"
8
#include "editorbuffertable.h"
9
#include "encoding.h"
10
#include "fileio.h"
11
#include "filetable.h"
12
#include "list.h"
13
#include "log.h"
14
#include "misc.h"
15
#include "proto.h"
16
#include "referenceableitemtable.h"
17
#include "referencerefresh.h"
18
#include "timestamp.h"
19
#include "undo.h"
20
#include "usage.h"
21

22

23
typedef struct editorMemoryBlock {
24
    struct editorMemoryBlock *next;
25
} EditorMemoryBlock;
26

27

28
#define MIN_EDITOR_MEMORY_BLOCK_SIZE_BITS 11 /* 11 bits will be 2^11 bytes in size */
29
#define MAX_EDITOR_MEMORY_BLOCK_SIZE_BITS 32 /* 32 bits will be 2^32 bytes in size */
30

31
// this has to cover at least alignment allocations
32
#define EDITOR_ALLOCATION_RESERVE 1024
33
#define EDITOR_FREE_PREFIX_SIZE 16
34

35
/* 32 entries, only 11-32 is used, pointers to allocated areas.
36
   I have no idea why this is so complicated, why not just use
37
   allocated memory, why keep it in this array? */
38
static EditorMemoryBlock *editorMemory[MAX_EDITOR_MEMORY_BLOCK_SIZE_BITS];
39

40

41
void editorInit(void) {
231✔
42
    initEditorBufferTable();
231✔
43
}
231✔
44

45
FileTimestamp editorFileModificationTime(char *path) {
1,026,951✔
46
    EditorBuffer *buffer;
1,026,951✔
47

48
    buffer = getEditorBufferForFile(path);
1,026,951✔
49
    if (buffer != NULL)
1,026,951✔
50
        return buffer->modificationTime;
1,019,017✔
51
    return fileModificationTime(path);
7,934✔
52
}
53

54
size_t editorFileSize(char *path) {
5✔
55
    EditorBuffer *buffer;
5✔
56

57
    buffer = getEditorBufferForFile(path);
5✔
58
    if (buffer != NULL)
5✔
59
        return buffer->size;
×
60
    return fileSize(path);
5✔
61
}
62

63
bool editorFileExists(char *path) {
1,039,641✔
64
    EditorBuffer *buffer;
1,039,641✔
65

66
    buffer = getEditorBufferForFile(path);
1,039,641✔
67
    if (buffer != NULL && buffer->preLoadedFromFile != NULL)
1,039,641✔
68
        return true;
69
    return fileExists(path);
1,039,290✔
70
}
71

72
static void editorError(int errCode, char *message) {
×
73
    errorMessage(errCode, message);
×
74
}
75

76
void freeTextSpace(char *space, int index) {
461,990✔
77
    EditorMemoryBlock *sp;
461,990✔
78
    sp = (EditorMemoryBlock *) space;
461,990✔
79
    sp->next = editorMemory[index];
461,990✔
80
    editorMemory[index] = sp;
461,990✔
81
}
461,990✔
82

83
void loadTextIntoEditorBuffer(EditorBuffer *buffer, FileTimestamp modificationTime, const char *text) {
4✔
84
    allocateNewEditorBufferTextSpace(buffer, strlen(text));
4✔
85
    strcpy(buffer->allocation.text, text);
4✔
86
}
4✔
87

88
void loadFileIntoEditorBuffer(EditorBuffer *buffer, FileTimestamp modificationTime, size_t fileSize) {
462,130✔
89
    allocateNewEditorBufferTextSpace(buffer, fileSize);
462,130✔
90

91
    char *text = buffer->allocation.text;
462,130✔
92
    assert(text != NULL);
93

462,130✔
94
    assert(buffer->preLoadedFromFile == NULL || buffer->preLoadedFromFile != buffer->fileName);
462,130✔
95
    int bufferSize = buffer->allocation.bufferSize;
96
    char *fileName = buffer->preLoadedFromFile? buffer->preLoadedFromFile: buffer->fileName;
462,130✔
97

98
    log_trace(":loading file %s==%s size %d", fileName, buffer->fileName, bufferSize);
462,130✔
99

462,130✔
100
    FILE *file = openFile(fileName, "r");
×
101
    if (file == NULL) {
102
        FATAL_ERROR(ERR_CANT_OPEN, fileName, EXIT_FAILURE);
103
    }
104

924,253✔
105
    int size = bufferSize;
924,253✔
106
    int n;
924,253✔
107
    do {
924,253✔
108
        n    = readFile(file, text, 1, size);
924,253✔
109
        text = text + n;
924,253✔
110
        size = size - n;
462,130✔
111
    } while (n>0);
112
    closeFile(file);
462,130✔
113

114
    if (size != 0) {
×
115
        // this is possible, due to <CR><LF> conversion under MS-DOS
×
116
        buffer->allocation.bufferSize -= size;
×
117
        if (size < 0) {
×
118
            char tmpBuffer[TMP_BUFF_SIZE];
×
119
            sprintf(tmpBuffer, "File %s: read %d chars of %d", fileName, bufferSize - size, bufferSize);
120
            editorError(ERR_INTERNAL, tmpBuffer);
121
        }
462,130✔
122
    }
462,130✔
123
    performEncodingAdjustments(buffer);
462,130✔
124
    buffer->modificationTime = modificationTime;
462,130✔
125
    buffer->size = fileSize;
462,130✔
126
    buffer->textLoaded = true;
127
}
128

462,163✔
129
// Should really be in editorbuffer but is dependent on many editor things...
462,163✔
130
void allocateNewEditorBufferTextSpace(EditorBuffer *buffer, int size) {
462,163✔
131
    int minSize = size + EDITOR_ALLOCATION_RESERVE + EDITOR_FREE_PREFIX_SIZE;
462,163✔
132
    int allocIndex = 11;
133
    int allocatedSize = 2048;
134

1,197,852✔
135
    // Ensure size to allocate is at least
735,689✔
136
    for(; allocatedSize<minSize; ) {
735,689✔
137
        allocIndex++;
138
        allocatedSize = allocatedSize << 1;
139
    }
462,163✔
140

462,163✔
141
    char *space = (char *)editorMemory[allocIndex];
3,044✔
142
    if (space == NULL) {
3,044✔
143
        space = malloc(allocatedSize+1);
×
144
        if (space == NULL)
145
            FATAL_ERROR(ERR_NO_MEMORY, "global malloc", EXIT_FAILURE);
3,044✔
146
        // put magic
147
        space[allocatedSize] = 0x3b;
459,119✔
148
    } else {
149
        editorMemory[allocIndex] = editorMemory[allocIndex]->next;
462,163✔
150
    }
151
    buffer->allocation = (EditorBufferAllocationData){.bufferSize = size, .text = space+EDITOR_FREE_PREFIX_SIZE,
152
                                           .allocatedFreePrefixSize = EDITOR_FREE_PREFIX_SIZE,
153
                                           .allocatedBlock = space, .allocatedIndex = allocIndex,
462,163✔
154
                                           .allocatedSize = allocatedSize};
155
}
509✔
156

157
void replaceStringInEditorBuffer(EditorBuffer *buffer, int offset, int deleteSize, char *string,
158
                                 int length, EditorUndo **undoP) {
509✔
159
    assert(offset >=0 && offset <= buffer->allocation.bufferSize);
509✔
160
    assert(deleteSize >= 0);
161
    assert(length >= 0);
162

×
163
    int oldSize = buffer->allocation.bufferSize;
164
    if (deleteSize+offset > oldSize) {
509✔
165
        // deleting over end of buffer,
166
        // delete only until end of buffer
509✔
167
        deleteSize = oldSize - offset;
168
    }
509✔
169
    log_trace("replacing string in buffer %d (%s)", buffer, buffer->fileName);
170

1✔
171
    int newSize = oldSize + length - deleteSize;
172
    // prepare operation
1✔
173
    if (newSize >= buffer->allocation.allocatedSize - buffer->allocation.allocatedFreePrefixSize) {
1✔
174
        // resize buffer
1✔
175
        log_trace("resizing %s from %d(%d) to %d", buffer->fileName, buffer->allocation.bufferSize,
1✔
176
                  buffer->allocation.allocatedSize, newSize);
1✔
177
        char *text = buffer->allocation.text;
1✔
178
        char *space = buffer->allocation.allocatedBlock;
1✔
179
        int index = buffer->allocation.allocatedIndex;
180
        allocateNewEditorBufferTextSpace(buffer, newSize);
181
        memcpy(buffer->allocation.text, text, oldSize);
509✔
182
        buffer->allocation.bufferSize = oldSize;
183
        freeTextSpace(space, index);
296✔
184
    }
296✔
185

296✔
186
    assert(newSize < buffer->allocation.allocatedSize - buffer->allocation.allocatedFreePrefixSize);
296✔
187
    if (undoP!=NULL) {
296✔
188
        // note undo information
296✔
189
        int undoSize = length;
190
        assert(deleteSize >= 0);
191
        char *undoText = malloc(deleteSize+1);
192
        memcpy(undoText, buffer->allocation.text+offset, deleteSize);
509✔
193
        undoText[deleteSize]=0;
509✔
194
        EditorUndo *u = newUndoReplace(buffer, offset, undoSize, deleteSize, undoText, *undoP);
509✔
195
        *undoP = u;
509✔
196
    }
197

198
    // edit text
509✔
199
    memmove(buffer->allocation.text+offset+length, buffer->allocation.text+offset+deleteSize,
175✔
200
            buffer->allocation.bufferSize - offset - deleteSize);
175✔
201
    memcpy(buffer->allocation.text+offset, string, length);
103✔
202
    buffer->allocation.bufferSize = buffer->allocation.bufferSize - deleteSize + length;
203

72✔
204
    // update markers
3,005✔
205
    if (deleteSize > length) {
2,830✔
206
        int pattractor;
1,332✔
207
        if (length > 0)
27✔
208
            pattractor = offset + length - 1;
209
        else
1,305✔
210
            pattractor = offset + length;
211
        for (EditorMarker *m=buffer->markers; m!=NULL; m=m->next) {
212
            if (m->offset >= offset + length) {
213
                if (m->offset < offset+deleteSize) {
214
                    m->offset = pattractor;
5,625✔
215
                } else {
5,291✔
216
                    m->offset = m->offset - deleteSize + length;
2,462✔
217
                }
218
            }
219
        }
220
    } else {
509✔
221
        for (EditorMarker *m=buffer->markers; m!=NULL; m=m->next) {
509✔
222
            if (m->offset >= offset + deleteSize) {
223
                m->offset = m->offset - deleteSize + length;
22✔
224
            }
225
        }
22✔
226
    }
7✔
227
    setEditorBufferModified(buffer);
3✔
228
}
×
229

×
230
void moveBlockInEditorBuffer(EditorMarker *sourceMarker, EditorMarker *destinationMarker, int size,
231
                             EditorUndo **undo) {
22✔
232
    assert(size>=0);
22✔
233
    if (destinationMarker->buffer == sourceMarker->buffer
234
        && destinationMarker->offset > sourceMarker->offset
235
        && destinationMarker->offset < sourceMarker->offset+size) {
22✔
236
        errorMessage(ERR_INTERNAL, "[editor] moving block to its original place");
22✔
237
        return;
22✔
238
    }
239
    EditorBuffer *sourceBuffer = sourceMarker->buffer;
240
    EditorBuffer *destinationBuffer = destinationMarker->buffer;
241

22✔
242
    // insert the block to target position
243
    int destinationOffset = destinationMarker->offset;
244
    int offset1 = sourceMarker->offset;
245
    int offset2 = offset1+size;
22✔
246
    assert(offset1 <= offset2);
22✔
247

22✔
248
    // do it at two steps for the case if source buffer equals target buffer
22✔
249
    // first just allocate space
250
    replaceStringInEditorBuffer(destinationBuffer, destinationOffset, 0, sourceBuffer->allocation.text + offset1,
22✔
251
                                offset2 - offset1, NULL);
252

253
    // now copy text
22✔
254
    offset1 = sourceMarker->offset;
22✔
255
    offset2 = offset1+size;
111✔
256
    replaceStringInEditorBuffer(destinationBuffer, destinationOffset, offset2 - offset1,
89✔
257
                                sourceBuffer->allocation.text + offset1, offset2 - offset1, NULL);
89✔
258
    // save target for undo;
57✔
259
    int undoOffset = sourceMarker->offset;
57✔
260

57✔
261
    // move all markers from moved block
262
    assert(offset1 == sourceMarker->offset);
263
    assert(offset2 == offset1+size);
264
    EditorMarker *mm = sourceBuffer->markers;
265
    while (mm!=NULL) {
22✔
266
        EditorMarker *tmp = mm->next;
267
        if (mm->offset>=offset1 && mm->offset<offset2) {
268
            removeEditorMarkerFromBufferWithoutFreeing(mm);
22✔
269
            mm->offset = destinationOffset + (mm->offset-offset1);
22✔
270
            attachMarkerToBuffer(mm, destinationBuffer);
271
        }
272
        mm = tmp;
22✔
273
    }
15✔
274
    // remove the source block
275
    replaceStringInEditorBuffer(sourceBuffer, offset1, offset2 - offset1, sourceBuffer->allocation.text + offset1,
276
                                0, NULL);
277
    //
278
    setEditorBufferModified(sourceBuffer);
75✔
279
    setEditorBufferModified(destinationBuffer);
75✔
280

75✔
281
    // add the whole operation into undo
75✔
282
    if (undo!=NULL) {
75✔
283
        *undo = newUndoMove(destinationBuffer, sourceMarker->offset, offset2-offset1, sourceBuffer, undoOffset,
75✔
284
                            *undo);
285
    }
128✔
286
}
128✔
287

128✔
288
static void quasiSaveEditorBuffer(EditorBuffer *buffer) {
289
    buffer->modifiedSinceLastQuasiSave = false;
1,324✔
290
    buffer->modificationTime = fileTimestampNow();
2,496✔
291
    FileItem *fileItem = getFileItemWithFileNumber(buffer->fileNumber);
1,300✔
292
    fileItem->lastModified = buffer->modificationTime;
45✔
293
}
45✔
294

295
void quasiSaveModifiedEditorBuffers(void) {
296
    bool saving = false;
297
    static FileTimestamp lastQuasiSaveTime = NULL_TIMESTAMP;
83✔
298

45✔
299
    for (int i = 0; i != -1; i = getNextExistingEditorBufferIndex(i + 1)) {
300
        for (EditorBufferList *ll = getEditorBufferListElementAt(i); ll != NULL; ll = ll->next) {
301
            if (ll->buffer->modifiedSinceLastQuasiSave) {
45✔
302
                saving = true;
45✔
303
                goto cont;
45✔
304
            }
45✔
305
        }
×
306
    }
×
307
cont:
45✔
308
    if (saving) {
35✔
309
        // sychronization, since last quazi save, there must
310
        // be at least one second, otherwise times will be wrong
311
        FileTimestamp currentTime = fileTimestampNow();
1,568✔
312
        long currentSeconds = fileTimestampSeconds(currentTime);
2,967✔
313
        long lastSaveSeconds = fileTimestampSeconds(lastQuasiSaveTime);
1,527✔
314
        if (lastSaveSeconds > currentSeconds + 5) {
75✔
315
            FATAL_ERROR(ERR_INTERNAL, "last save in the future, travelling in time?",
316
                        EXIT_FAILURE);
317
        } else if (lastSaveSeconds >= currentSeconds) {
318
            sleep(1 + lastSaveSeconds - currentSeconds);
128✔
319
        }
128✔
320
    }
321
    for (int i = 0; i != -1; i = getNextExistingEditorBufferIndex(i + 1)) {
714✔
322
        for (EditorBufferList *ll = getEditorBufferListElementAt(i); ll != NULL; ll = ll->next) {
3,088✔
323
            if (ll->buffer->modifiedSinceLastQuasiSave) {
4,270✔
324
                quasiSaveEditorBuffer(ll->buffer);
1,896✔
325
            }
50✔
326
        }
50✔
327
    }
50✔
328
    lastQuasiSaveTime = fileTimestampNow();
329
}
330

331
void loadAllOpenedEditorBuffers(void) {
332
    for (int i = 0; i != -1; i = getNextExistingEditorBufferIndex(i + 1)) {
714✔
333
        for (EditorBufferList *l = getEditorBufferListElementAt(i); l != NULL; l = l->next) {
334
            if (!l->buffer->textLoaded) {
310✔
335
                assert(l->buffer->preLoadedFromFile == NULL || l->buffer->preLoadedFromFile != l->buffer->fileName);
310✔
336
                char *fileName = l->buffer->preLoadedFromFile? l->buffer->preLoadedFromFile: l->buffer->fileName;
×
337
                if (fileExists(fileName)) {
310✔
338
                    loadFileIntoEditorBuffer(l->buffer, fileModificationTime(fileName), fileSize(fileName));
339
                    log_trace("loading '%s' into '%s'", fileName, l->buffer->fileName);
340
                }
239✔
341
            }
342
        }
239✔
343
    }
239✔
344
}
239✔
345

239✔
346
static Reference *skipInvisibleReferences(Reference *reference) {
347
    while (reference != NULL && !isVisibleUsage(reference->usage))
54✔
348
        reference = reference->next;
54✔
349
    return reference;
54✔
350
}
351

179✔
352
static void addMarkerAndAdvance(EditorBuffer *buffer, int maxoffset, EditorMarkerList **markerList,
71✔
353
                                Reference **reference) {
71✔
354
    EditorMarker *m = newEditorMarker(buffer, maxoffset);
71✔
355
    *markerList = newEditorMarkerList(m, (*reference)->usage, *markerList);
71✔
356
    *reference = skipInvisibleReferences((*reference)->next);
71✔
357
}
71✔
358

×
359
EditorMarkerList *convertReferencesToEditorMarkers(Reference *references) {
×
360
    EditorMarkerList *markerList = NULL;
×
361
    Reference        *reference = references;
362

71✔
363
    while (reference != NULL) {
71✔
364
        reference = skipInvisibleReferences(reference);
71✔
365
        if (reference != NULL) {
71✔
366
            int           file     = reference->position.file;
367
            FileItem     *fileItem = getFileItemWithFileNumber(file);
71✔
368
            EditorBuffer *buffer   = findOrCreateAndLoadEditorBufferForFile(fileItem->name);
71✔
369
            if (buffer == NULL) {
107,081✔
370
                errorMessage(ERR_CANT_OPEN, fileItem->name);
107,080✔
371
                while (reference != NULL && file == reference->position.file)
372
                    reference = reference->next;
107,010✔
373
            } else {
237✔
374
                char *text      = buffer->allocation.text;
375
                char *smax      = text + buffer->allocation.bufferSize;
107,010✔
376
                int   maxoffset = buffer->allocation.bufferSize - 1;
3,030✔
377
                if (maxoffset < 0)
3,030✔
378
                    maxoffset = 0;
379
                int line = 1;
380
                int col  = 0;
381
                for (; text < smax; text++, col++) {
382
                    if (reference == NULL || file != reference->position.file)
383
                        break;
384
                    if (line == reference->position.line && col == reference->position.col) {
1✔
385
                        addMarkerAndAdvance(buffer, text - buffer->allocation.text, &markerList, &reference);
386
                    }
3,030✔
387
                    if (*text == '\n') {
3,030✔
388
                        if (reference != NULL && file == reference->position.file
389
                            && line == reference->position.line) {
390
                            /* col unreachable on this line — clamp marker to
391
                             * current (end-of-line) position and advance to
72✔
392
                             * the next reference. Mirrors the past-EOF
1✔
393
                             * fallback below, but caught at line scope to
394
                             * avoid the cascade where one stuck ref dooms
395
                             * every subsequent ref to maxoffset. */
396
                            addMarkerAndAdvance(buffer, text - buffer->allocation.text, &markerList, &reference);
397
                        }
398
                        line++;
399
                        col = -1;
293✔
400
                    }
54✔
401
                }
402
                // references beyond end of buffer
403
                while (reference != NULL && file == reference->position.file) {
×
404
                    addMarkerAndAdvance(buffer, maxoffset, &markerList, &reference);
×
405
                }
406
            }
407
        }
2✔
408
    }
409
    // get markers in the same order as were references
410
    // ?? is this still needed?
2✔
411
    LIST_REVERSE(EditorMarkerList, markerList);
2✔
412
    return markerList;
3✔
413
}
1✔
414

1✔
415
static int referencePositionComparer(Reference *r1, Reference *r2) {
1✔
416
    return positionIsLessThan(r1->position, r2->position);
1✔
417
}
1✔
418

1✔
419
Reference *convertEditorMarkersToReferences(EditorMarkerList **editorMarkerListP) {
825✔
420

825✔
421
    LIST_MERGE_SORT(EditorMarkerList, *editorMarkerListP, editorMarkerListBefore);
1✔
422

1✔
423
    Reference *reference = NULL;
424
    EditorMarkerList *markers = *editorMarkerListP;
×
425
    while (markers!=NULL) {
426
        EditorBuffer *buffer = markers->marker->buffer;
824✔
427
        char *text = buffer->allocation.text;
32✔
428
        char *textMax = text + buffer->allocation.bufferSize;
32✔
429
        char *offset = buffer->allocation.text + markers->marker->offset;
430
        int line = 1;
431
        int col = 0;
×
432
        for (; text<textMax; text++, col++) {
×
433
            if (text == offset) {
434
                reference = newReference((Position){buffer->fileNumber, line, col}, markers->usage, reference);
435
                markers = markers->next;
2✔
436
                if (markers==NULL || markers->marker->buffer != buffer)
437
                    break;
438
                offset = buffer->allocation.text + markers->marker->offset;
439
            }
440
            if (*text=='\n') {
441
                line++;
442
                col = -1;
443
            }
444
        }
445
        while (markers!=NULL && markers->marker->buffer==buffer) {
446
            reference = newReference((Position){buffer->fileNumber, line, 0}, markers->usage, reference);
447
            markers = markers->next;
448
        }
449
    }
450
    LIST_MERGE_SORT(Reference, reference, referencePositionComparer);
451
    return reference;
452
}
453

454
#if 0
455
void editorDumpBuffer(EditorBuffer *buff) {
456
    /* TODO: Should really put this in log() */
457
    for (int i=0; i<buff->allocation.bufferSize; i++) {
458
        putc(buff->allocation.text[i], errOut);
459
    }
460
}
461

462
void editorDumpUndoList(EditorUndo *undo) {
463
    log_trace("[dumping editor undo list]");
464
    while (undo != NULL) {
465
        switch (undo->operation) {
466
        case UNDO_REPLACE_STRING:
467
            log_trace("replace string [%s:%d] %d (%ld)%s %d", undo->buffer->name, undo->u.replace.offset,
468
                      undo->u.replace.size, (unsigned long)undo->u.replace.str, undo->u.replace.str,
469
                      undo->u.replace.strlen);
470
            if (strlen(undo->u.replace.str) != undo->u.replace.strlen)
471
                log_trace("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
472
            break;
473
        case UNDO_RENAME_BUFFER:
5,648✔
474
            log_trace("rename buffer %s %s", undo->buffer->name, undo->u.rename.name);
5,648✔
475
            break;
253,161✔
476
        case UNDO_MOVE_BLOCK:
707,388✔
477
            log_trace("move block [%s:%d] [%s:%d] size==%d", undo->buffer->name, undo->u.moveBlock.offset,
459,875✔
478
                      undo->u.moveBlock.dbuffer->name, undo->u.moveBlock.doffset, undo->u.moveBlock.size);
459,875✔
479
            break;
459,875✔
480
        default:
459,875✔
481
            errorMessage(ERR_INTERNAL, "Unknown operation to undo");
482
        }
483
        undo = undo->next;
5,648✔
484
    }
485
    log_trace("[undodump] end");
486
}
5,648✔
487
#endif
5,648✔
488

465,523✔
489
static EditorBufferList *computeListOfAllEditorBuffers(void) {
459,875✔
490
    EditorBufferList *list = NULL;
459,875✔
491
    for (int i=0; i != -1 ; i = getNextExistingEditorBufferIndex(i+1)) {
459,875✔
492
        for (EditorBufferList *l = getEditorBufferListElementAt(i); l != NULL; l = l->next) {
493
            EditorBufferList *element;
5,648✔
494
            element = malloc(sizeof(EditorBufferList));
495
            *element = (EditorBufferList){.buffer = l->buffer, .next = list};
×
496
            list     = element;
×
497
        }
498
    }
499
    return list;
6✔
500
}
6✔
501

6✔
502
static void freeEditorBufferListButNotBuffers(EditorBufferList *list) {
6✔
503
    EditorBufferList *l = list;
2✔
504
    while (l!=NULL) {
505
        EditorBufferList *n = l->next;
506
        free(l);
507
        l = n;
508
    }
509
}
518✔
510

511
static int editorBufferNameLess(EditorBufferList*l1, EditorBufferList*l2) {
512
    return strcmp(l1->buffer->fileName, l2->buffer->fileName);
513
}
514

515
static bool directoryPrefixMatches(char *fileName, char *dirname) {
516
    int dirNameLength = strlen(dirname);
517
    return filenameCompare(fileName, dirname, dirNameLength) == 0
518
        && (fileName[dirNameLength] == '/'
519
            || fileName[dirNameLength] == '\\');
520
}
521

518✔
522
// TODO, do all this stuff better!
523
// This is still quadratic on number of opened buffers
518✔
524
// for recursive search
525
bool editorMapOnNonExistantFiles(char *dirname,
518✔
526
                                void (*fun)(MAP_FUN_SIGNATURE),
524✔
527
                                SearchDepth depth,
6✔
528
                                char *a1,
2✔
529
                                char *a2,
2✔
530
                                Completions *a3,
2✔
531
                                void *a4,
2✔
532
                                int *a5
×
533
) {
×
534
    // In order to avoid mapping of the same directory several
×
535
    // times, first just create list of all files, sort it, and then
×
536
    // map them
×
537
    EditorBufferList *listOfAllBuffers   = computeListOfAllEditorBuffers();
×
538
    LIST_MERGE_SORT(EditorBufferList, listOfAllBuffers, editorBufferNameLess);
539

×
540
    EditorBufferList *list = listOfAllBuffers;
×
541

×
542
    bool found = false;
543
    while(list!=NULL) {
544
        if (directoryPrefixMatches(list->buffer->fileName, dirname)) {
2✔
545
            int dirNameLength = strlen(dirname);
2✔
546
            char fname[MAX_FILE_NAME_SIZE];
547
            int fileNameLength;
548
            if (depth == DEPTH_ONE) {
2✔
549
                char *pathDelimiter = strchr(list->buffer->fileName+dirNameLength+1, '/');
550
                if (pathDelimiter==NULL)
×
551
                    pathDelimiter = strchr(list->buffer->fileName+dirNameLength+1, '\\');
×
552
                if (pathDelimiter==NULL) {
553
                    strcpy(fname, list->buffer->fileName+dirNameLength+1);
×
554
                    fileNameLength = strlen(fname);
×
555
                } else {
×
556
                    fileNameLength = pathDelimiter-(list->buffer->fileName+dirNameLength+1);
×
557
                    strncpy(fname, list->buffer->fileName+dirNameLength+1, fileNameLength);
×
558
                    fname[fileNameLength]=0;
×
559
                }
×
560
            } else {
561
                strcpy(fname, list->buffer->fileName+dirNameLength+1);
562
                fileNameLength = strlen(fname);
2✔
563
            }
564
            // Only map on nonexistant files
565
            if (!fileExists(list->buffer->fileName)) {
4✔
566
                // get file name
567
                (*fun)(fname, a1, a2, a3, a4, a5);
568
                found = true;
518✔
569
                // skip all files in the same directory
570
                char *lastMapped = list->buffer->fileName;
518✔
571
                int lastMappedLength = dirNameLength+1+fileNameLength;
572
                list = list->next;
573
                while (list!=NULL
462,154✔
574
                       && filenameCompare(list->buffer->fileName, lastMapped, lastMappedLength)==0
462,154✔
575
                       && (list->buffer->fileName[lastMappedLength]=='/' || list->buffer->fileName[lastMappedLength]=='\\')) {
462,082✔
576
                    list = list->next;
924,133✔
577
                }
578
            } else {
579
                list = list->next;
3,632✔
580
            }
3,632✔
581
        } else {
3,632✔
582
            list = list->next;
3,630✔
583
        }
3,632✔
584
    }
3,632✔
585
    freeEditorBufferListButNotBuffers(listOfAllBuffers);
586

4,045✔
587
    return found;
4,045✔
588
}
462,567✔
589

458,522✔
590
static bool bufferIsCloseable(EditorBuffer *buffer) {
458,322✔
591
    return buffer != NULL && buffer->textLoaded && buffer->markers==NULL
592
        && buffer->preLoadedFromFile == NULL  /* not -preloaded */
458,322✔
593
        && ! buffer->modified;
458,322✔
594
}
595

596
void closeEditorBufferIfCloseable(char *name) {
4,045✔
597
    EditorBuffer *buffer = findOrCreateAndLoadEditorBufferForFile(name);
4,045✔
598
    if (bufferIsCloseable(buffer))
599
        deregisterEditorBuffer(name);
539✔
600
    freeEditorBuffer(buffer);
539✔
601
}
737✔
602

198✔
603
void closeAllEditorBuffersIfClosable(void) {
604
    EditorBufferList *allEditorBuffers = computeListOfAllEditorBuffers();
539✔
605
    for (EditorBufferList *l = allEditorBuffers; l!=NULL; l=l->next) {
539✔
606
        if (bufferIsCloseable(l->buffer)) {
607
            log_trace("closable %d for '%s'='%s'", bufferIsCloseable(l->buffer),
546✔
608
                      l->buffer->preLoadedFromFile?l->buffer->preLoadedFromFile:"(null)", l->buffer->fileName);
546✔
609
            EditorBuffer *buffer = deregisterEditorBuffer(l->buffer->fileName);
1,695✔
610
            freeEditorBuffer(buffer);
1,149✔
611
        }
19✔
612
    }
19✔
613
    freeEditorBufferListButNotBuffers(allEditorBuffers);
19✔
614
}
19✔
615

616
void clearPreloadedThisRequestFlags(void) {
617
    EditorBufferList *allEditorBuffers = computeListOfAllEditorBuffers();
618
    for (EditorBufferList *l = allEditorBuffers; l != NULL; l = l->next) {
619
        l->buffer->preloadedThisRequest = false;
19✔
620
    }
621
    freeEditorBufferListButNotBuffers(allEditorBuffers);
622
}
546✔
623

546✔
624
void closeEditorBuffersNoLongerPreloaded(ArgumentsVector baseArgs) {
625
    EditorBufferList *allEditorBuffers = computeListOfAllEditorBuffers();
626
    for (EditorBufferList *l = allEditorBuffers; l != NULL; l = l->next) {
627
        if (isPreloaded(l->buffer) && !l->buffer->preloadedThisRequest) {
628
            log_trace("Closing ghost preloaded buffer '%s' (fileNumber=%d)", l->buffer->fileName, l->buffer->fileNumber);
629
            int fileNumber = l->buffer->fileNumber;
630
            EditorBuffer *buffer = deregisterEditorBuffer(l->buffer->fileName);
631
            freeEditorBuffer(buffer);
632
            /* Refresh the file's references from disk content, since
633
             * the editor's view is no longer authoritative. For a CU
634
             * this re-parses the file; for a header it strips refs and
635
             * relies on a CU includer's reparse to re-emit them. */
636
            reparseFile(fileNumber, baseArgs);
637
        }
638
    }
639
    freeEditorBufferListButNotBuffers(allEditorBuffers);
640
}
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