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

Tehreer / SheenBidi / 29167177394

11 Jul 2026 08:31PM UTC coverage: 96.086% (-0.09%) from 96.177%
29167177394

push

github

mta452
[lib] Invalidate paragraph user info on attribute changes

2 of 2 new or added lines in 1 file covered. (100.0%)

42 existing lines in 4 files now uncovered.

4051 of 4216 relevant lines covered (96.09%)

444218.68 hits per line

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

91.91
/Source/API/SBText.c
1
/*
2
 * Copyright (C) 2025-2026 Muhammad Tayyab Akram
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
#include <API/SBBase.h>
18

19
#if SB_TEXT_API_SUPPORTED
20

21
#include <stddef.h>
22

23
#include <API/SBAssert.h>
24
#include <API/SBAttributeRegistry.h>
25
#include <API/SBTextConfig.h>
26
#include <API/SBTextIterators.h>
27
#include <Core/Object.h>
28
#include <Text/AttributeManager.h>
29
#include <Text/BidiTypesBuffer.h>
30
#include <Text/TextAnalysis.h>
31
#include <Text/TextBuffer.h>
32

33
#include "SBText.h"
34

35
/* =========================================================================
36
 * Text Implementation
37
 * ========================================================================= */
38

39
SBTextRef SBTextCreate(const void *string, SBUInteger length, SBStringEncoding encoding,
17✔
40
    SBTextConfigRef config)
41
{
42
    SBMutableTextRef text = SBTextCreateMutable(encoding, config);
17✔
43

44
    if (text) {
17✔
45
        SBTextAppendCodeUnits(text, string, length);
17✔
46
        text->isMutable = SBFalse;
17✔
47
    }
48

49
    return text;
17✔
50
}
51

52
SBTextRef SBTextCreateCopy(SBTextRef text)
2✔
53
{
54
    SBMutableTextRef copy = SBTextCreateMutableCopy(text);
2✔
55

56
    if (copy) {
2✔
57
        copy->isMutable = SBFalse;
2✔
58
    }
59

60
    return copy;
2✔
61
}
62

63
SBStringEncoding SBTextGetEncoding(SBTextRef text)
7✔
64
{
65
    return text->buffer.encoding;
7✔
66
}
67

68
SBAttributeRegistryRef SBTextGetAttributeRegistry(SBTextRef text)
10✔
69
{
70
    return text->attributeRegistry;
10✔
71
}
72

73
SBUInteger SBTextGetLength(SBTextRef text)
24✔
74
{
75
    return TextBufferGetLength((TextBufferRef)&text->buffer);
24✔
76
}
77

78
void SBTextGetCodeUnits(SBTextRef text, SBUInteger index, SBUInteger length, void *buffer)
10✔
79
{
80
    SBBoolean isRangeValid = SBUIntegerVerifyRange(text->buffer.codeUnits.count, index, length);
10✔
81

82
    SBAssert(isRangeValid);
10✔
83

84
    TextBufferGetCodeUnits((TextBufferRef)&text->buffer, index, length, buffer);
10✔
85
}
10✔
86

87
void SBTextGetBidiTypes(SBTextRef text, SBUInteger index, SBUInteger length, SBBidiType *buffer)
2✔
88
{
89
    SBBoolean isRangeValid = SBUIntegerVerifyRange(text->buffer.codeUnits.count, index, length);
2✔
90

91
    SBAssert(isRangeValid);
2✔
92

93
    BidiTypesBufferGetBidiTypes((BidiTypesBufferRef)&text->bidiTypes, index, length, buffer);
2✔
94
}
2✔
95

96
void SBTextGetScripts(SBTextRef text, SBUInteger index, SBUInteger length, SBScript *buffer)
2✔
97
{
98
    SBBoolean isRangeValid = SBUIntegerVerifyRange(text->buffer.codeUnits.count, index, length);
2✔
99

100
    SBAssert(isRangeValid && !text->isEditing);
2✔
101

102
    TextAnalysisGetScripts((TextAnalysisRef)&text->analysis, index, length, buffer);
2✔
103
}
2✔
104

105
void SBTextGetResolvedLevels(SBTextRef text, SBUInteger index, SBUInteger length, SBLevel *buffer)
2✔
106
{
107
    SBBoolean isRangeValid = SBUIntegerVerifyRange(text->buffer.codeUnits.count, index, length);
2✔
108

109
    SBAssert(isRangeValid && !text->isEditing);
2✔
110

111
    TextAnalysisGetResolvedLevels((TextAnalysisRef)&text->analysis, index, length, buffer);
2✔
112
}
2✔
113

114
void SBTextGetCodeUnitParagraphInfo(SBTextRef text, SBUInteger index,
17✔
115
    SBParagraphInfo *paragraphInfo)
116
{
117
    SBBoolean isValidIndex = index < text->buffer.codeUnits.count;
17✔
118

119
    SBAssert(isValidIndex && !text->isEditing);
17✔
120

121
    TextAnalysisGetCodeUnitParagraphInfo((TextAnalysisRef)&text->analysis, index, paragraphInfo);
17✔
122
}
17✔
123

124
SBParagraphIteratorRef SBTextCreateParagraphIterator(SBTextRef text)
2✔
125
{
126
    return SBParagraphIteratorCreate(text);
2✔
127
}
128

129
SBLogicalRunIteratorRef SBTextCreateLogicalRunIterator(SBTextRef text)
1✔
130
{
131
    return SBLogicalRunIteratorCreate(text);
1✔
132
}
133

134
SBScriptRunIteratorRef SBTextCreateScriptRunIterator(SBTextRef text)
1✔
135
{
136
    return SBScriptRunIteratorCreate(text);
1✔
137
}
138

139
SBAttributeRunIteratorRef SBTextCreateAttributeRunIterator(SBTextRef text)
26✔
140
{
141
    return SBAttributeRunIteratorCreate(text);
26✔
142
}
143

144
SBVisualRunIteratorRef SBTextCreateVisualRunIterator(SBTextRef text,
×
145
    SBUInteger index, SBUInteger length)
146
{
147
    SBVisualRunIteratorRef iterator = SBVisualRunIteratorCreate(text);
×
148

149
    if (iterator) {
×
150
        SBVisualRunIteratorReset(iterator, index, length);
×
151
    }
152

153
    return iterator;
×
154
}
155

156
SBTextRef SBTextRetain(SBTextRef text)
90✔
157
{
158
    return ObjectRetain((ObjectRef)text);
90✔
159
}
160

161
void SBTextRelease(SBTextRef text)
271✔
162
{
163
    ObjectRelease((ObjectRef)text);
271✔
164
}
271✔
165

166
/* =========================================================================
167
 * Mutable Text Implementation
168
 * ========================================================================= */
169

170
static void FinalizeMutableText(ObjectRef object)
181✔
171
{
172
    SBMutableTextRef text = object;
181✔
173

174
    AttributeManagerFinalize(&text->attributeManager);
181✔
175
    TextAnalysisFinalize(&text->analysis);
181✔
176
    BidiTypesBufferFinalize(&text->bidiTypes);
181✔
177
    TextBufferFinalize(&text->buffer);
181✔
178

179
    if (text->attributeRegistry) {
181✔
180
        SBAttributeRegistryRelease(text->attributeRegistry);
127✔
181
    }
182
}
181✔
183

184
SB_INTERNAL SBMutableTextRef SBTextCreateMutableWithParameters(SBStringEncoding encoding,
181✔
185
    SBAttributeRegistryRef attributeRegistry, SBLevel baseLevel,
186
    const SBParagraphUserInfoCallbacks *userInfoCallbacks,
187
    SBParagraphUserInfoProviderCallback userInfoProvider, void *userInfoProviderContext)
188
{
189
    const SBUInteger size = sizeof(SBText);
181✔
190
    void *pointer = NULL;
181✔
191
    SBMutableTextRef text;
192

193
    text = ObjectCreate(&size, 1, &pointer, FinalizeMutableText);
181✔
194

195
    if (text) {
181✔
196
        if (attributeRegistry) {
181✔
197
            attributeRegistry = SBAttributeRegistryRetain(attributeRegistry);
127✔
198
        }
199

200
        text->isMutable = SBTrue;
181✔
201
        text->isEditing = SBFalse;
181✔
202
        text->attributeRegistry = attributeRegistry;
181✔
203

204
        TextBufferInitialize(&text->buffer, encoding);
181✔
205
        BidiTypesBufferInitialize(&text->bidiTypes);
181✔
206
        TextAnalysisInitialize(&text->analysis, text, baseLevel, userInfoCallbacks,
181✔
207
            userInfoProvider, userInfoProviderContext);
208
        AttributeManagerInitialize(&text->attributeManager, &text->analysis, attributeRegistry);
181✔
209
    }
210

211
    return text;
181✔
212
}
213

214
SBMutableTextRef SBTextCreateMutable(SBStringEncoding encoding, SBTextConfigRef config)
176✔
215
{
216
    SBMutableTextRef text = SBTextCreateMutableWithParameters(encoding,
352✔
217
        config->attributeRegistry, config->baseLevel, &config->userInfoCallbacks,
176✔
218
        config->userInfoProvider, config->userInfoProviderContext);
219

220
    if (text) {
221
        /* TODO: Apply default attributes */
222
    }
223

224
    return text;
176✔
225
}
226

227
SBMutableTextRef SBTextCreateMutableCopy(SBTextRef text)
5✔
228
{
229
    SBMutableTextRef copy = SBTextCreateMutableWithParameters(text->buffer.encoding,
10✔
230
        text->attributeRegistry, text->analysis.baseLevel, &text->analysis.userInfoCallbacks,
5✔
231
        text->analysis.userInfoProvider, text->analysis.userInfoProviderContext);
5✔
232

233
    if (copy) {
5✔
234
        TextBufferCopyCodeUnits(&copy->buffer, &text->buffer);
5✔
235
        BidiTypesBufferCopyBidiTypes(&copy->bidiTypes, &text->bidiTypes);
5✔
236
        TextAnalysisCopyParagraphs(&copy->analysis, &text->analysis);
5✔
237
        TextAnalysisFlush(&copy->analysis, &copy->buffer, &copy->bidiTypes);
5✔
238

239
        /* Copy attributes */
240
        AttributeManagerCopyAttributes(&copy->attributeManager, &text->attributeManager);
5✔
241
    }
242

243
    return copy;
5✔
244
}
245

246
void SBTextBeginEditing(SBMutableTextRef text)
5✔
247
{
248
    SBAssert(text->isMutable);
5✔
249

250
    if (!text->isMutable) {
5✔
UNCOV
251
        return;
×
252
    }
253

254
    text->isEditing = SBTrue;
5✔
255
}
256

257
void SBTextEndEditing(SBMutableTextRef text)
5✔
258
{
259
    SBAssert(text->isMutable);
5✔
260

261
    if (!text->isMutable) {
5✔
UNCOV
262
        return;
×
263
    }
264

265
    TextAnalysisFlush(&text->analysis, &text->buffer, &text->bidiTypes);
5✔
266
    text->isEditing = SBFalse;
5✔
267
}
268

269
void SBTextAppendCodeUnits(SBMutableTextRef text,
169✔
270
    const void *codeUnitBuffer, SBUInteger codeUnitCount)
271
{
272
    SBAssert(text->isMutable);
169✔
273

274
    if (!text->isMutable) {
169✔
UNCOV
275
        return;
×
276
    }
277

278
    SBTextInsertCodeUnits(text, text->buffer.codeUnits.count, codeUnitBuffer, codeUnitCount);
169✔
279
}
280

281
void SBTextInsertCodeUnits(SBMutableTextRef text, SBUInteger index,
203✔
282
    const void *codeUnitBuffer, SBUInteger codeUnitCount)
283
{
284
    SBAssert(text->isMutable && index <= text->buffer.codeUnits.count);
203✔
285

286
    if (!text->isMutable) {
203✔
UNCOV
287
        return;
×
288
    }
289

290
    if (codeUnitCount > 0) {
203✔
291
        /* Splice into the raw code-unit storage */
292
        TextBufferInsertCodeUnits(&text->buffer, index, codeUnitBuffer, codeUnitCount);
194✔
293

294
        /* Re-derive bidi types immediately; paragraph boundaries need them as input */
295
        BidiTypesBufferReplaceRange(&text->bidiTypes, &text->buffer, index, 0, codeUnitCount);
194✔
296

297
        /* Re-derive paragraph boundaries; scripts/levels stay deferred */
298
        TextAnalysisReplaceRange(&text->analysis, &text->buffer, &text->bidiTypes, index, 0, codeUnitCount);
194✔
299

300
        /* Reserve attribute manager space */
301
        AttributeManagerReserveRange(&text->attributeManager, index, codeUnitCount);
194✔
302

303
        /* Perform immediate analysis if not in batch editing mode */
304
        if (!text->isEditing) {
194✔
305
            TextAnalysisFlush(&text->analysis, &text->buffer, &text->bidiTypes);
186✔
306
        }
307
    }
308
}
309

310
void SBTextDeleteCodeUnits(SBMutableTextRef text, SBUInteger index, SBUInteger length)
17✔
311
{
312
    SBUInteger rangeEnd = index + length;
17✔
313
    SBBoolean isRangeValid = (rangeEnd <= text->buffer.codeUnits.count && index <= rangeEnd);
17✔
314

315
    SBAssert(text->isMutable && isRangeValid);
17✔
316

317
    if (!text->isMutable) {
17✔
UNCOV
318
        return;
×
319
    }
320

321
    if (length > 0) {
17✔
322
        TextBufferDeleteCodeUnits(&text->buffer, index, length);
16✔
323
        BidiTypesBufferReplaceRange(&text->bidiTypes, &text->buffer, index, length, 0);
16✔
324
        TextAnalysisReplaceRange(&text->analysis, &text->buffer, &text->bidiTypes, index, length, 0);
16✔
325
        AttributeManagerRemoveRange(&text->attributeManager, index, length);
16✔
326

327
        if (!text->isEditing) {
16✔
328
            /* Perform immediate analysis if not in batch editing mode */
329
            TextAnalysisFlush(&text->analysis, &text->buffer, &text->bidiTypes);
15✔
330
        }
331
    }
332
}
333

334
void SBTextSetCodeUnits(SBMutableTextRef text,
3✔
335
    const void *codeUnitBuffer, SBUInteger codeUnitCount)
336
{
337
    SBAssert(text->isMutable);
3✔
338

339
    if (!text->isMutable) {
3✔
UNCOV
340
        return;
×
341
    }
342

343
    SBTextReplaceCodeUnits(text, 0, text->buffer.codeUnits.count, codeUnitBuffer, codeUnitCount);
3✔
344
}
345

346
void SBTextReplaceCodeUnits(SBMutableTextRef text, SBUInteger index, SBUInteger length,
28✔
347
    const void *codeUnitBuffer, SBUInteger codeUnitCount)
348
{
349
    SBUInteger rangeEnd = index + length;
28✔
350
    SBBoolean isRangeValid = (rangeEnd <= text->buffer.codeUnits.count && index <= rangeEnd);
28✔
351

352
    SBAssert(text->isMutable && isRangeValid);
28✔
353

354
    if (!text->isMutable) {
28✔
UNCOV
355
        return;
×
356
    }
357

358
    if (length > 0 || codeUnitCount > 0) {
28✔
359
        TextBufferReplaceCodeUnits(&text->buffer, index, length, codeUnitBuffer, codeUnitCount);
26✔
360
        BidiTypesBufferReplaceRange(&text->bidiTypes, &text->buffer, index, length, codeUnitCount);
26✔
361
        TextAnalysisReplaceRange(&text->analysis, &text->buffer, &text->bidiTypes, index, length, codeUnitCount);
26✔
362
        AttributeManagerReplaceRange(&text->attributeManager, index, length, codeUnitCount);
26✔
363

364
        if (!text->isEditing) {
26✔
365
            /* Perform immediate analysis if not in batch editing mode */
366
            TextAnalysisFlush(&text->analysis, &text->buffer, &text->bidiTypes);
25✔
367
        }
368
    }
369
}
370

371
void SBTextSetAttribute(SBMutableTextRef text, SBUInteger index, SBUInteger length,
111✔
372
    SBAttributeID attributeID, const void *attributeValue)
373
{
374
    SBUInteger rangeEnd = index + length;
111✔
375
    SBBoolean isRangeValid = (rangeEnd <= text->buffer.codeUnits.count && index <= rangeEnd);
111✔
376

377
    SBAssert(text->isMutable && isRangeValid);
111✔
378

379
    if (!text->isMutable) {
111✔
UNCOV
380
        return;
×
381
    }
382

383
    if (length > 0) {
111✔
384
        AttributeManagerSetAttribute(&text->attributeManager,
108✔
385
            index, length, attributeID, attributeValue);
386
    }
387
}
388

389
void SBTextRemoveAttribute(SBMutableTextRef text, SBUInteger index, SBUInteger length,
15✔
390
    SBAttributeID attributeID)
391
{
392
    SBUInteger rangeEnd = index + length;
15✔
393
    SBBoolean isRangeValid = (rangeEnd <= text->buffer.codeUnits.count && index <= rangeEnd);
15✔
394

395
    SBAssert(text->isMutable && isRangeValid);
15✔
396

397
    if (!text->isMutable) {
15✔
UNCOV
398
        return;
×
399
    }
400

401
    if (length > 0) {
15✔
402
        AttributeManagerRemoveAttribute(&text->attributeManager, index, length, attributeID);
13✔
403
    }
404
}
405

406
#endif
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