diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-06-25 16:26:53 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-06-25 16:26:53 +0300 |
commit | 5dbc85eaaa1bd0a0fc11dd76a75ece2efe763df5 (patch) | |
tree | 9721fb7aced603adb10b9bb3f3beb3f8d5fba973 /src/gmdocument.c | |
parent | 95c527db1484f7758a180c6de051d0182c3b2e81 (diff) | |
parent | f99a9111170f2ff28383fd3172fdaf4b9a1ba069 (diff) |
Merge branch 'work/v1.6' into work/serious-unicode
# Conflicts:
# res/fonts/SmolEmoji-Regular.ttf
Diffstat (limited to 'src/gmdocument.c')
-rw-r--r-- | src/gmdocument.c | 137 |
1 files changed, 112 insertions, 25 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c index b95f85e7..f15d9d1d 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -21,6 +21,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
22 | 22 | ||
23 | #include "gmdocument.h" | 23 | #include "gmdocument.h" |
24 | #include "gmtypesetter.h" | ||
24 | #include "gmutil.h" | 25 | #include "gmutil.h" |
25 | #include "lang.h" | 26 | #include "lang.h" |
26 | #include "ui/color.h" | 27 | #include "ui/color.h" |
@@ -48,7 +49,7 @@ iBool isDark_GmDocumentTheme(enum iGmDocumentTheme d) { | |||
48 | iDeclareType(GmLink) | 49 | iDeclareType(GmLink) |
49 | 50 | ||
50 | struct Impl_GmLink { | 51 | struct Impl_GmLink { |
51 | iString url; | 52 | iString url; /* resolved */ |
52 | iRangecc urlRange; /* URL in the source */ | 53 | iRangecc urlRange; /* URL in the source */ |
53 | iRangecc labelRange; /* label in the source */ | 54 | iRangecc labelRange; /* label in the source */ |
54 | iRangecc labelIcon; /* special icon defined in the label text */ | 55 | iRangecc labelIcon; /* special icon defined in the label text */ |
@@ -74,9 +75,10 @@ iDefineTypeConstruction(GmLink) | |||
74 | 75 | ||
75 | struct Impl_GmDocument { | 76 | struct Impl_GmDocument { |
76 | iObject object; | 77 | iObject object; |
77 | enum iGmDocumentFormat format; | 78 | enum iSourceFormat format; |
78 | iString source; | 79 | iString unormSource; /* unnormalized source */ |
79 | iString url; /* for resolving relative links */ | 80 | iString source; /* normalized source */ |
81 | iString url; /* for resolving relative links */ | ||
80 | iString localHost; | 82 | iString localHost; |
81 | iInt2 size; | 83 | iInt2 size; |
82 | iArray layout; /* contents of source, laid out in document space */ | 84 | iArray layout; /* contents of source, laid out in document space */ |
@@ -90,12 +92,14 @@ struct Impl_GmDocument { | |||
90 | iChar siteIcon; | 92 | iChar siteIcon; |
91 | iMedia * media; | 93 | iMedia * media; |
92 | iStringSet *openURLs; /* currently open URLs for highlighting links */ | 94 | iStringSet *openURLs; /* currently open URLs for highlighting links */ |
95 | iBool isPaletteValid; | ||
96 | iColor palette[tmMax_ColorId]; /* copy of the color palette */ | ||
93 | }; | 97 | }; |
94 | 98 | ||
95 | iDefineObjectConstruction(GmDocument) | 99 | iDefineObjectConstruction(GmDocument) |
96 | 100 | ||
97 | static enum iGmLineType lineType_GmDocument_(const iGmDocument *d, const iRangecc line) { | 101 | static enum iGmLineType lineType_GmDocument_(const iGmDocument *d, const iRangecc line) { |
98 | if (d->format == plainText_GmDocumentFormat) { | 102 | if (d->format == plainText_SourceFormat) { |
99 | return text_GmLineType; | 103 | return text_GmLineType; |
100 | } | 104 | } |
101 | return lineType_Rangecc(line); | 105 | return lineType_Rangecc(line); |
@@ -254,9 +258,12 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li | |||
254 | if ((len = decodeBytes_MultibyteChar(desc.start, desc.end, &icon)) > 0) { | 258 | if ((len = decodeBytes_MultibyteChar(desc.start, desc.end, &icon)) > 0) { |
255 | if (desc.start + len < desc.end && | 259 | if (desc.start + len < desc.end && |
256 | (isPictograph_Char(icon) || isEmoji_Char(icon) || | 260 | (isPictograph_Char(icon) || isEmoji_Char(icon) || |
261 | /* TODO: Add range(s) of 0x2nnn symbols. */ | ||
262 | icon == 0x2139 /* info */ || | ||
257 | icon == 0x2191 /* up arrow */ || | 263 | icon == 0x2191 /* up arrow */ || |
264 | icon == 0x2022 /* bullet */ || | ||
258 | icon == 0x2a2f /* close X */ || | 265 | icon == 0x2a2f /* close X */ || |
259 | icon == 0x2022 /* bullet */) && | 266 | icon == 0x2b50) && |
260 | !isFitzpatrickType_Char(icon)) { | 267 | !isFitzpatrickType_Char(icon)) { |
261 | link->flags |= iconFromLabel_GmLinkFlag; | 268 | link->flags |= iconFromLabel_GmLinkFlag; |
262 | link->labelIcon = (iRangecc){ desc.start, desc.start + len }; | 269 | link->labelIcon = (iRangecc){ desc.start, desc.start + len }; |
@@ -282,6 +289,12 @@ static void clearLinks_GmDocument_(iGmDocument *d) { | |||
282 | clear_PtrArray(&d->links); | 289 | clear_PtrArray(&d->links); |
283 | } | 290 | } |
284 | 291 | ||
292 | static iBool isGopher_GmDocument_(const iGmDocument *d) { | ||
293 | const iRangecc scheme = urlScheme_String(&d->url); | ||
294 | return (equalCase_Rangecc(scheme, "gopher") || | ||
295 | equalCase_Rangecc(scheme, "finger")); | ||
296 | } | ||
297 | |||
285 | static iBool isForcedMonospace_GmDocument_(const iGmDocument *d) { | 298 | static iBool isForcedMonospace_GmDocument_(const iGmDocument *d) { |
286 | const iRangecc scheme = urlScheme_String(&d->url); | 299 | const iRangecc scheme = urlScheme_String(&d->url); |
287 | if (equalCase_Rangecc(scheme, "gemini")) { | 300 | if (equalCase_Rangecc(scheme, "gemini")) { |
@@ -305,7 +318,7 @@ static void linkContentWasLaidOut_GmDocument_(iGmDocument *d, const iGmMediaInfo | |||
305 | 318 | ||
306 | static iBool isNormalized_GmDocument_(const iGmDocument *d) { | 319 | static iBool isNormalized_GmDocument_(const iGmDocument *d) { |
307 | const iPrefs *prefs = prefs_App(); | 320 | const iPrefs *prefs = prefs_App(); |
308 | if (d->format == plainText_GmDocumentFormat) { | 321 | if (d->format == plainText_SourceFormat) { |
309 | return iTrue; /* tabs are always normalized in plain text */ | 322 | return iTrue; /* tabs are always normalized in plain text */ |
310 | } | 323 | } |
311 | if (startsWithCase_String(&d->url, "gemini:") && prefs->monospaceGemini) { | 324 | if (startsWithCase_String(&d->url, "gemini:") && prefs->monospaceGemini) { |
@@ -357,6 +370,7 @@ static void updateOpenURLs_GmDocument_(iGmDocument *d) { | |||
357 | static void doLayout_GmDocument_(iGmDocument *d) { | 370 | static void doLayout_GmDocument_(iGmDocument *d) { |
358 | const iPrefs *prefs = prefs_App(); | 371 | const iPrefs *prefs = prefs_App(); |
359 | const iBool isMono = isForcedMonospace_GmDocument_(d); | 372 | const iBool isMono = isForcedMonospace_GmDocument_(d); |
373 | const iBool isGopher = isGopher_GmDocument_(d); | ||
360 | const iBool isNarrow = d->size.x < 90 * gap_Text; | 374 | const iBool isNarrow = d->size.x < 90 * gap_Text; |
361 | const iBool isVeryNarrow = d->size.x <= 70 * gap_Text; | 375 | const iBool isVeryNarrow = d->size.x <= 70 * gap_Text; |
362 | const iBool isExtremelyNarrow = d->size.x <= 60 * gap_Text; | 376 | const iBool isExtremelyNarrow = d->size.x <= 60 * gap_Text; |
@@ -434,12 +448,15 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
434 | enum iGmLineType prevType = text_GmLineType; | 448 | enum iGmLineType prevType = text_GmLineType; |
435 | enum iGmLineType prevNonBlankType = text_GmLineType; | 449 | enum iGmLineType prevNonBlankType = text_GmLineType; |
436 | iBool followsBlank = iFalse; | 450 | iBool followsBlank = iFalse; |
437 | if (d->format == plainText_GmDocumentFormat) { | 451 | if (d->format == plainText_SourceFormat) { |
438 | isPreformat = iTrue; | 452 | isPreformat = iTrue; |
439 | isFirstText = iFalse; | 453 | isFirstText = iFalse; |
440 | } | 454 | } |
441 | while (nextSplit_Rangecc(content, "\n", &contentLine)) { | 455 | while (nextSplit_Rangecc(content, "\n", &contentLine)) { |
442 | iRangecc line = contentLine; /* `line` will be trimmed later; would confuse nextSplit */ | 456 | iRangecc line = contentLine; /* `line` will be trimmed; modifying would confuse `nextSplit_Rangecc` */ |
457 | if (*line.end == '\r') { | ||
458 | line.end--; /* trim CR always */ | ||
459 | } | ||
443 | iGmRun run = { .color = white_ColorId }; | 460 | iGmRun run = { .color = white_ColorId }; |
444 | enum iGmLineType type; | 461 | enum iGmLineType type; |
445 | float indent = 0.0f; | 462 | float indent = 0.0f; |
@@ -472,7 +489,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
472 | meta.flags = constValue_Array(oldPreMeta, preIndex, iGmPreMeta).flags & | 489 | meta.flags = constValue_Array(oldPreMeta, preIndex, iGmPreMeta).flags & |
473 | folded_GmPreMetaFlag; | 490 | folded_GmPreMetaFlag; |
474 | } | 491 | } |
475 | else if (prefs->collapsePreOnLoad) { | 492 | else if (prefs->collapsePreOnLoad && !isGopher) { |
476 | meta.flags |= folded_GmPreMetaFlag; | 493 | meta.flags |= folded_GmPreMetaFlag; |
477 | } | 494 | } |
478 | pushBack_Array(&d->preMeta, &meta); | 495 | pushBack_Array(&d->preMeta, &meta); |
@@ -500,14 +517,14 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
500 | if (contentLine.start == content.start) { | 517 | if (contentLine.start == content.start) { |
501 | prevType = type; | 518 | prevType = type; |
502 | } | 519 | } |
503 | if (d->format == gemini_GmDocumentFormat && | 520 | if (d->format == gemini_SourceFormat && |
504 | startsWithSc_Rangecc(line, "```", &iCaseSensitive)) { | 521 | startsWithSc_Rangecc(line, "```", &iCaseSensitive)) { |
505 | isPreformat = iFalse; | 522 | isPreformat = iFalse; |
506 | addSiteBanner = iFalse; /* overrides the banner */ | 523 | addSiteBanner = iFalse; /* overrides the banner */ |
507 | continue; | 524 | continue; |
508 | } | 525 | } |
509 | run.preId = preId; | 526 | run.preId = preId; |
510 | run.font = (d->format == plainText_GmDocumentFormat ? regularMonospace_FontId : preFont); | 527 | run.font = (d->format == plainText_SourceFormat ? regularMonospace_FontId : preFont); |
511 | indent = indents[type]; | 528 | indent = indents[type]; |
512 | } | 529 | } |
513 | if (addSiteBanner) { | 530 | if (addSiteBanner) { |
@@ -580,7 +597,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
580 | } | 597 | } |
581 | } | 598 | } |
582 | /* Folded blocks are represented by a single run with the alt text. */ | 599 | /* Folded blocks are represented by a single run with the alt text. */ |
583 | if (isPreformat && d->format != plainText_GmDocumentFormat) { | 600 | if (isPreformat && d->format != plainText_SourceFormat) { |
584 | const iGmPreMeta *meta = constAt_Array(&d->preMeta, preId - 1); | 601 | const iGmPreMeta *meta = constAt_Array(&d->preMeta, preId - 1); |
585 | if (meta->flags & folded_GmPreMetaFlag) { | 602 | if (meta->flags & folded_GmPreMetaFlag) { |
586 | const iBool isBlank = isEmpty_Range(&meta->altText); | 603 | const iBool isBlank = isEmpty_Range(&meta->altText); |
@@ -676,7 +693,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
676 | pushBack_Array(&d->layout, &icon); | 693 | pushBack_Array(&d->layout, &icon); |
677 | } | 694 | } |
678 | run.color = colors[type]; | 695 | run.color = colors[type]; |
679 | if (d->format == plainText_GmDocumentFormat) { | 696 | if (d->format == plainText_SourceFormat) { |
680 | run.color = colors[text_GmLineType]; | 697 | run.color = colors[text_GmLineType]; |
681 | } | 698 | } |
682 | /* Special formatting for the first paragraph (e.g., subtitle, introduction, or lede). */ | 699 | /* Special formatting for the first paragraph (e.g., subtitle, introduction, or lede). */ |
@@ -705,8 +722,8 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
705 | type == quote_GmLineType ? 4 : 0); | 722 | type == quote_GmLineType ? 4 : 0); |
706 | } | 723 | } |
707 | const iBool isWordWrapped = | 724 | const iBool isWordWrapped = |
708 | (d->format == plainText_GmDocumentFormat ? prefs->plainTextWrap : !isPreformat); | 725 | (d->format == plainText_SourceFormat ? prefs->plainTextWrap : !isPreformat); |
709 | if (isPreformat && d->format != plainText_GmDocumentFormat) { | 726 | if (isPreformat && d->format != plainText_SourceFormat) { |
710 | /* Remember the top left coordinates of the block (first line of block). */ | 727 | /* Remember the top left coordinates of the block (first line of block). */ |
711 | iGmPreMeta *meta = at_Array(&d->preMeta, preId - 1); | 728 | iGmPreMeta *meta = at_Array(&d->preMeta, preId - 1); |
712 | if (~meta->flags & topLeft_GmPreMetaFlag) { | 729 | if (~meta->flags & topLeft_GmPreMetaFlag) { |
@@ -859,10 +876,13 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
859 | } | 876 | } |
860 | } | 877 | } |
861 | } | 878 | } |
879 | printf("[GmDocument] layout size: %zu runs (%zu bytes)\n", | ||
880 | size_Array(&d->layout), size_Array(&d->layout) * sizeof(iGmRun)); | ||
862 | } | 881 | } |
863 | 882 | ||
864 | void init_GmDocument(iGmDocument *d) { | 883 | void init_GmDocument(iGmDocument *d) { |
865 | d->format = gemini_GmDocumentFormat; | 884 | d->format = gemini_SourceFormat; |
885 | init_String(&d->unormSource); | ||
866 | init_String(&d->source); | 886 | init_String(&d->source); |
867 | init_String(&d->url); | 887 | init_String(&d->url); |
868 | init_String(&d->localHost); | 888 | init_String(&d->localHost); |
@@ -878,6 +898,8 @@ void init_GmDocument(iGmDocument *d) { | |||
878 | d->siteIcon = 0; | 898 | d->siteIcon = 0; |
879 | d->media = new_Media(); | 899 | d->media = new_Media(); |
880 | d->openURLs = NULL; | 900 | d->openURLs = NULL; |
901 | d->isPaletteValid = iFalse; | ||
902 | iZap(d->palette); | ||
881 | } | 903 | } |
882 | 904 | ||
883 | void deinit_GmDocument(iGmDocument *d) { | 905 | void deinit_GmDocument(iGmDocument *d) { |
@@ -893,6 +915,7 @@ void deinit_GmDocument(iGmDocument *d) { | |||
893 | deinit_String(&d->localHost); | 915 | deinit_String(&d->localHost); |
894 | deinit_String(&d->url); | 916 | deinit_String(&d->url); |
895 | deinit_String(&d->source); | 917 | deinit_String(&d->source); |
918 | deinit_String(&d->unormSource); | ||
896 | } | 919 | } |
897 | 920 | ||
898 | iMedia *media_GmDocument(iGmDocument *d) { | 921 | iMedia *media_GmDocument(iGmDocument *d) { |
@@ -903,6 +926,11 @@ const iMedia *constMedia_GmDocument(const iGmDocument *d) { | |||
903 | return d->media; | 926 | return d->media; |
904 | } | 927 | } |
905 | 928 | ||
929 | const iString *url_GmDocument(const iGmDocument *d) { | ||
930 | return &d->url; | ||
931 | } | ||
932 | |||
933 | #if 0 | ||
906 | void reset_GmDocument(iGmDocument *d) { | 934 | void reset_GmDocument(iGmDocument *d) { |
907 | clear_Media(d->media); | 935 | clear_Media(d->media); |
908 | clearLinks_GmDocument_(d); | 936 | clearLinks_GmDocument_(d); |
@@ -911,8 +939,11 @@ void reset_GmDocument(iGmDocument *d) { | |||
911 | clear_Array(&d->preMeta); | 939 | clear_Array(&d->preMeta); |
912 | clear_String(&d->url); | 940 | clear_String(&d->url); |
913 | clear_String(&d->localHost); | 941 | clear_String(&d->localHost); |
942 | clear_String(&d->source); | ||
943 | clear_String(&d->unormSource); | ||
914 | d->themeSeed = 0; | 944 | d->themeSeed = 0; |
915 | } | 945 | } |
946 | #endif | ||
916 | 947 | ||
917 | static void setDerivedThemeColors_(enum iGmDocumentTheme theme) { | 948 | static void setDerivedThemeColors_(enum iGmDocumentTheme theme) { |
918 | set_Color(tmQuoteIcon_ColorId, | 949 | set_Color(tmQuoteIcon_ColorId, |
@@ -1385,9 +1416,23 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { | |||
1385 | } | 1416 | } |
1386 | printf("---\n"); | 1417 | printf("---\n"); |
1387 | #endif | 1418 | #endif |
1419 | /* Color functions operate on the global palette for convenience, but we may need to switch | ||
1420 | palettes on the fly if more than one GmDocument is being displayed simultaneously. */ | ||
1421 | memcpy(d->palette, get_Root()->tmPalette, sizeof(d->palette)); | ||
1422 | d->isPaletteValid = iTrue; | ||
1423 | } | ||
1424 | |||
1425 | void makePaletteGlobal_GmDocument(const iGmDocument *d) { | ||
1426 | if (d->isPaletteValid) { | ||
1427 | memcpy(get_Root()->tmPalette, d->palette, sizeof(d->palette)); | ||
1428 | } | ||
1388 | } | 1429 | } |
1389 | 1430 | ||
1390 | void setFormat_GmDocument(iGmDocument *d, enum iGmDocumentFormat format) { | 1431 | void invalidatePalette_GmDocument(iGmDocument *d) { |
1432 | d->isPaletteValid = iFalse; | ||
1433 | } | ||
1434 | |||
1435 | void setFormat_GmDocument(iGmDocument *d, enum iSourceFormat format) { | ||
1391 | d->format = format; | 1436 | d->format = format; |
1392 | } | 1437 | } |
1393 | 1438 | ||
@@ -1413,6 +1458,9 @@ iBool updateOpenURLs_GmDocument(iGmDocument *d) { | |||
1413 | const iBool isOpen = contains_StringSet(d->openURLs, &link->url); | 1458 | const iBool isOpen = contains_StringSet(d->openURLs, &link->url); |
1414 | if (isOpen ^ ((link->flags & isOpen_GmLinkFlag) != 0)) { | 1459 | if (isOpen ^ ((link->flags & isOpen_GmLinkFlag) != 0)) { |
1415 | iChangeFlags(link->flags, isOpen_GmLinkFlag, isOpen); | 1460 | iChangeFlags(link->flags, isOpen_GmLinkFlag, isOpen); |
1461 | if (isOpen) { | ||
1462 | link->flags |= visited_GmLinkFlag; | ||
1463 | } | ||
1416 | wasChanged = iTrue; | 1464 | wasChanged = iTrue; |
1417 | } | 1465 | } |
1418 | } | 1466 | } |
@@ -1429,10 +1477,12 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
1429 | iRangecc src = range_String(&d->source); | 1477 | iRangecc src = range_String(&d->source); |
1430 | iRangecc line = iNullRange; | 1478 | iRangecc line = iNullRange; |
1431 | iBool isPreformat = iFalse; | 1479 | iBool isPreformat = iFalse; |
1432 | if (d->format == plainText_GmDocumentFormat) { | 1480 | if (d->format == plainText_SourceFormat) { |
1433 | isPreformat = iTrue; /* Cannot be turned off. */ | 1481 | isPreformat = iTrue; /* Cannot be turned off. */ |
1434 | } | 1482 | } |
1435 | const int preTabWidth = 4; /* TODO: user-configurable parameter */ | 1483 | const int preTabWidth = 4; /* TODO: user-configurable parameter */ |
1484 | iBool wasNormalized = iFalse; | ||
1485 | iBool hasTabs = iFalse; | ||
1436 | while (nextSplit_Rangecc(src, "\n", &line)) { | 1486 | while (nextSplit_Rangecc(src, "\n", &line)) { |
1437 | if (isPreformat) { | 1487 | if (isPreformat) { |
1438 | /* Replace any tab characters with spaces for visualization. */ | 1488 | /* Replace any tab characters with spaces for visualization. */ |
@@ -1443,13 +1493,19 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
1443 | while (numSpaces-- > 0) { | 1493 | while (numSpaces-- > 0) { |
1444 | appendCStrN_String(normalized, " ", 1); | 1494 | appendCStrN_String(normalized, " ", 1); |
1445 | } | 1495 | } |
1496 | hasTabs = iTrue; | ||
1497 | wasNormalized = iTrue; | ||
1446 | } | 1498 | } |
1447 | else if (*ch != '\r') { | 1499 | else if (*ch != '\v') { |
1448 | appendCStrN_String(normalized, ch, 1); | 1500 | appendCStrN_String(normalized, ch, 1); |
1449 | } | 1501 | } |
1502 | else { | ||
1503 | hasTabs = iTrue; | ||
1504 | wasNormalized = iTrue; | ||
1505 | } | ||
1450 | } | 1506 | } |
1451 | appendCStr_String(normalized, "\n"); | 1507 | appendCStr_String(normalized, "\n"); |
1452 | if (d->format == gemini_GmDocumentFormat && | 1508 | if (d->format == gemini_SourceFormat && |
1453 | lineType_GmDocument_(d, line) == preformatted_GmLineType) { | 1509 | lineType_GmDocument_(d, line) == preformatted_GmLineType) { |
1454 | isPreformat = iFalse; | 1510 | isPreformat = iFalse; |
1455 | } | 1511 | } |
@@ -1465,7 +1521,10 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
1465 | int spaceCount = 0; | 1521 | int spaceCount = 0; |
1466 | for (const char *ch = line.start; ch != line.end; ch++) { | 1522 | for (const char *ch = line.start; ch != line.end; ch++) { |
1467 | char c = *ch; | 1523 | char c = *ch; |
1468 | if (c == '\r') continue; | 1524 | if (c == '\v') { |
1525 | wasNormalized = iTrue; | ||
1526 | continue; | ||
1527 | } | ||
1469 | if (isNormalizableSpace_(c)) { | 1528 | if (isNormalizableSpace_(c)) { |
1470 | if (isPrevSpace) { | 1529 | if (isPrevSpace) { |
1471 | if (++spaceCount == 8) { | 1530 | if (++spaceCount == 8) { |
@@ -1474,9 +1533,13 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
1474 | popBack_Block(&normalized->chars); | 1533 | popBack_Block(&normalized->chars); |
1475 | pushBack_Block(&normalized->chars, '\t'); | 1534 | pushBack_Block(&normalized->chars, '\t'); |
1476 | } | 1535 | } |
1536 | wasNormalized = iTrue; | ||
1477 | continue; /* skip repeated spaces */ | 1537 | continue; /* skip repeated spaces */ |
1478 | } | 1538 | } |
1479 | c = ' '; | 1539 | if (c != ' ') { |
1540 | c = ' '; | ||
1541 | wasNormalized = iTrue; | ||
1542 | } | ||
1480 | isPrevSpace = iTrue; | 1543 | isPrevSpace = iTrue; |
1481 | } | 1544 | } |
1482 | else { | 1545 | else { |
@@ -1487,8 +1550,14 @@ static void normalize_GmDocument(iGmDocument *d) { | |||
1487 | } | 1550 | } |
1488 | appendCStr_String(normalized, "\n"); | 1551 | appendCStr_String(normalized, "\n"); |
1489 | } | 1552 | } |
1553 | printf("hasTabs: %d\n", hasTabs); | ||
1554 | printf("wasNormalized: %d\n", wasNormalized); | ||
1555 | fflush(stdout); | ||
1490 | set_String(&d->source, collect_String(normalized)); | 1556 | set_String(&d->source, collect_String(normalized)); |
1491 | normalize_String(&d->source); /* NFC */ | 1557 | normalize_String(&d->source); /* NFC */ |
1558 | printf("orig:%zu norm:%zu\n", size_String(&d->unormSource), size_String(&d->source)); | ||
1559 | /* normalized source has an extra newline at the end */ | ||
1560 | // iAssert(wasNormalized || equal_String(&d->unormSource, &d->source)); | ||
1492 | } | 1561 | } |
1493 | 1562 | ||
1494 | void setUrl_GmDocument(iGmDocument *d, const iString *url) { | 1563 | void setUrl_GmDocument(iGmDocument *d, const iString *url) { |
@@ -1499,8 +1568,18 @@ void setUrl_GmDocument(iGmDocument *d, const iString *url) { | |||
1499 | updateIconBasedOnUrl_GmDocument_(d); | 1568 | updateIconBasedOnUrl_GmDocument_(d); |
1500 | } | 1569 | } |
1501 | 1570 | ||
1502 | void setSource_GmDocument(iGmDocument *d, const iString *source, int width) { | 1571 | void setSource_GmDocument(iGmDocument *d, const iString *source, int width, |
1503 | set_String(&d->source, source); | 1572 | enum iGmDocumentUpdate updateType) { |
1573 | printf("[GmDocument] source update (%zu bytes), width:%d, final:%d\n", | ||
1574 | size_String(source), width, updateType == final_GmDocumentUpdate); | ||
1575 | if (size_String(source) == size_String(&d->unormSource)) { | ||
1576 | iAssert(equal_String(source, &d->unormSource)); | ||
1577 | printf("[GmDocument] source is unchanged!\n"); | ||
1578 | return; /* Nothing to do. */ | ||
1579 | } | ||
1580 | set_String(&d->unormSource, source); | ||
1581 | /* Normalize. */ | ||
1582 | set_String(&d->source, &d->unormSource); | ||
1504 | if (isNormalized_GmDocument_(d)) { | 1583 | if (isNormalized_GmDocument_(d)) { |
1505 | normalize_GmDocument(d); | 1584 | normalize_GmDocument(d); |
1506 | } | 1585 | } |
@@ -1602,6 +1681,14 @@ const iString *source_GmDocument(const iGmDocument *d) { | |||
1602 | return &d->source; | 1681 | return &d->source; |
1603 | } | 1682 | } |
1604 | 1683 | ||
1684 | size_t memorySize_GmDocument(const iGmDocument *d) { | ||
1685 | return size_String(&d->unormSource) + | ||
1686 | size_String(&d->source) + | ||
1687 | size_Array(&d->layout) * sizeof(iGmRun) + | ||
1688 | size_Array(&d->links) * sizeof(iGmLink) + | ||
1689 | memorySize_Media(d->media); | ||
1690 | } | ||
1691 | |||
1605 | iRangecc findText_GmDocument(const iGmDocument *d, const iString *text, const char *start) { | 1692 | iRangecc findText_GmDocument(const iGmDocument *d, const iString *text, const char *start) { |
1606 | const char * src = constBegin_String(&d->source); | 1693 | const char * src = constBegin_String(&d->source); |
1607 | const size_t startPos = (start ? start - src : 0); | 1694 | const size_t startPos = (start ? start - src : 0); |