diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-11-28 14:03:33 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-11-28 14:03:33 +0200 |
commit | 1efe2aab4fdc811c1dd37c76d1f5d37063745649 (patch) | |
tree | 410df70002df7256343a993fc91a9b1dcbb3dbca /src/bookmarks.c | |
parent | ab44b19cf747c348099c5aa86fcaf8bf0be628c9 (diff) |
Bookmarks: Internal tags have a dot prefix
Internal behavior tags are now written in bookmarks.ini with a dot prefix (like hidden files on Unix), and at runtime they are removed from the tags string.
This makes things more efficient as it isn't necessary to compile regular expressions all the time.
TODO: Add "Edit Feed..." into the Bookmarks context menu, and a new menu item for listing all subscriptions.
IssueID #331
Diffstat (limited to 'src/bookmarks.c')
-rw-r--r-- | src/bookmarks.c | 135 |
1 files changed, 113 insertions, 22 deletions
diff --git a/src/bookmarks.c b/src/bookmarks.c index d4b20162..2606d975 100644 --- a/src/bookmarks.c +++ b/src/bookmarks.c | |||
@@ -37,6 +37,7 @@ void init_Bookmark(iBookmark *d) { | |||
37 | init_String(&d->url); | 37 | init_String(&d->url); |
38 | init_String(&d->title); | 38 | init_String(&d->title); |
39 | init_String(&d->tags); | 39 | init_String(&d->tags); |
40 | iZap(d->flags); | ||
40 | iZap(d->when); | 41 | iZap(d->when); |
41 | d->parentId = 0; | 42 | d->parentId = 0; |
42 | d->order = 0; | 43 | d->order = 0; |
@@ -48,6 +49,7 @@ void deinit_Bookmark(iBookmark *d) { | |||
48 | deinit_String(&d->url); | 49 | deinit_String(&d->url); |
49 | } | 50 | } |
50 | 51 | ||
52 | #if 0 | ||
51 | iBool hasTag_Bookmark(const iBookmark *d, const char *tag) { | 53 | iBool hasTag_Bookmark(const iBookmark *d, const char *tag) { |
52 | if (!d) return iFalse; | 54 | if (!d) return iFalse; |
53 | iRegExp *pattern = new_RegExp(format_CStr("\\b%s\\b", tag), caseSensitive_RegExpOption); | 55 | iRegExp *pattern = new_RegExp(format_CStr("\\b%s\\b", tag), caseSensitive_RegExpOption); |
@@ -60,7 +62,7 @@ iBool hasTag_Bookmark(const iBookmark *d, const char *tag) { | |||
60 | 62 | ||
61 | void addTag_Bookmark(iBookmark *d, const char *tag) { | 63 | void addTag_Bookmark(iBookmark *d, const char *tag) { |
62 | if (!isEmpty_String(&d->tags)) { | 64 | if (!isEmpty_String(&d->tags)) { |
63 | appendChar_String(&d->tags, ' '); | 65 | appendCStr_String(&d->tags, " "); |
64 | } | 66 | } |
65 | appendCStr_String(&d->tags, tag); | 67 | appendCStr_String(&d->tags, tag); |
66 | } | 68 | } |
@@ -72,6 +74,93 @@ void removeTag_Bookmark(iBookmark *d, const char *tag) { | |||
72 | trim_String(&d->tags); | 74 | trim_String(&d->tags); |
73 | } | 75 | } |
74 | } | 76 | } |
77 | #endif | ||
78 | |||
79 | static struct { | ||
80 | uint32_t bit; | ||
81 | const char *tag; | ||
82 | iRegExp * pattern; | ||
83 | iRegExp * oldPattern; | ||
84 | } | ||
85 | specialTags_[] = { | ||
86 | { homepage_BookmarkFlag, ".homepage" }, | ||
87 | { remoteSource_BookmarkFlag, ".remotesource" }, | ||
88 | { linkSplit_BookmarkFlag, ".linksplit" }, | ||
89 | { userIcon_BookmarkFlag, ".usericon" }, | ||
90 | { subscribed_BookmarkFlag, ".subscribed" }, | ||
91 | { headings_BookmarkFlag, ".headings" }, | ||
92 | { ignoreWeb_BookmarkFlag, ".ignoreweb" }, | ||
93 | /* `remote_BookmarkFlag` not included because it's runtime only */ | ||
94 | }; | ||
95 | |||
96 | static void updatePatterns_(size_t index) { | ||
97 | if (!specialTags_[index].pattern) { | ||
98 | specialTags_[index].pattern = new_RegExp(format_CStr("(?<!\\w)\\%s\\b(?!\\w)", | ||
99 | specialTags_[index].tag), | ||
100 | caseSensitive_RegExpOption); /* never released */ | ||
101 | } | ||
102 | if (!specialTags_[index].oldPattern) { | ||
103 | /* TODO: Get rid of these when compatibility with v1.9 or older is not important. */ | ||
104 | specialTags_[index].oldPattern = | ||
105 | new_RegExp(format_CStr("\\b%s\\b", specialTags_[index].tag + 1), /* dotless */ | ||
106 | caseSensitive_RegExpOption); /* never released */ | ||
107 | } | ||
108 | } | ||
109 | |||
110 | static void normalizeSpacesInTags_(iString *tags) { | ||
111 | iBool wasSpace = iFalse; | ||
112 | iString out; | ||
113 | init_String(&out); | ||
114 | for (const char *ch = constBegin_String(tags); ch != constEnd_String(tags); ch++) { | ||
115 | if (*ch == ' ') { | ||
116 | if (!wasSpace) { | ||
117 | wasSpace = iTrue; | ||
118 | } | ||
119 | else { | ||
120 | continue; | ||
121 | } | ||
122 | } | ||
123 | else { | ||
124 | wasSpace = iFalse; | ||
125 | } | ||
126 | appendData_Block(&out.chars, ch, 1); | ||
127 | } | ||
128 | trim_String(&out); | ||
129 | set_String(tags, &out); | ||
130 | deinit_String(&out); | ||
131 | } | ||
132 | |||
133 | static void unpackDotTags_Bookmark_(iBookmark *d) { | ||
134 | iZap(d->flags); | ||
135 | iForIndices(i, specialTags_) { | ||
136 | updatePatterns_(i); | ||
137 | iRegExpMatch m; | ||
138 | init_RegExpMatch(&m); | ||
139 | iBool isSet = matchString_RegExp(specialTags_[i].pattern, &d->tags, &m); | ||
140 | if (!isSet) { | ||
141 | init_RegExpMatch(&m); | ||
142 | isSet = matchString_RegExp(specialTags_[i].oldPattern, &d->tags, &m); | ||
143 | } | ||
144 | iChangeFlags(d->flags, specialTags_[i].bit, isSet); | ||
145 | if (isSet) { | ||
146 | remove_Block(&d->tags.chars, m.range.start, size_Range(&m.range)); | ||
147 | } | ||
148 | } | ||
149 | normalizeSpacesInTags_(&d->tags); | ||
150 | } | ||
151 | |||
152 | static iString *packedDotTags_Bookmark_(const iBookmark *d) { | ||
153 | iString *withDot = copy_String(&d->tags); | ||
154 | iForIndices(i, specialTags_) { | ||
155 | if (d->flags & specialTags_[i].bit) { | ||
156 | if (!isEmpty_String(withDot)) { | ||
157 | appendCStr_String(withDot, " "); | ||
158 | } | ||
159 | appendCStr_String(withDot, specialTags_[i].tag); | ||
160 | } | ||
161 | } | ||
162 | return withDot; | ||
163 | } | ||
75 | 164 | ||
76 | iDefineTypeConstruction(Bookmark) | 165 | iDefineTypeConstruction(Bookmark) |
77 | 166 | ||
@@ -176,6 +265,7 @@ static void loadOldFormat_Bookmarks(iBookmarks *d, const char *dirPath) { | |||
176 | setRange_String(&bm->title, line); | 265 | setRange_String(&bm->title, line); |
177 | nextSplit_Rangecc(src, "\n", &line); | 266 | nextSplit_Rangecc(src, "\n", &line); |
178 | setRange_String(&bm->tags, line); | 267 | setRange_String(&bm->tags, line); |
268 | unpackDotTags_Bookmark_(bm); | ||
179 | insert_Bookmarks_(d, bm); | 269 | insert_Bookmarks_(d, bm); |
180 | } | 270 | } |
181 | } | 271 | } |
@@ -220,6 +310,10 @@ static void handleKeyValue_BookmarkLoader_(void *context, const iString *table, | |||
220 | } | 310 | } |
221 | else if (!cmp_String(key, "tags") && tv->type == string_TomlType) { | 311 | else if (!cmp_String(key, "tags") && tv->type == string_TomlType) { |
222 | set_String(&bm->tags, tv->value.string); | 312 | set_String(&bm->tags, tv->value.string); |
313 | if (strstr(cstr_String(&bm->tags), "subscribed")) { | ||
314 | printf("a\n"); | ||
315 | } | ||
316 | unpackDotTags_Bookmark_(bm); | ||
223 | } | 317 | } |
224 | else if (!cmp_String(key, "icon") && tv->type == int64_TomlType) { | 318 | else if (!cmp_String(key, "icon") && tv->type == int64_TomlType) { |
225 | bm->icon = (iChar) tv->value.int64; | 319 | bm->icon = (iChar) tv->value.int64; |
@@ -292,7 +386,6 @@ void load_Bookmarks(iBookmarks *d, const char *dirPath) { | |||
292 | 386 | ||
293 | void save_Bookmarks(const iBookmarks *d, const char *dirPath) { | 387 | void save_Bookmarks(const iBookmarks *d, const char *dirPath) { |
294 | lock_Mutex(d->mtx); | 388 | lock_Mutex(d->mtx); |
295 | iRegExp *remotePattern = iClob(new_RegExp("\\bremote\\b", caseSensitive_RegExpOption)); | ||
296 | iFile *f = newCStr_File(concatPath_CStr(dirPath, fileName_Bookmarks_)); | 389 | iFile *f = newCStr_File(concatPath_CStr(dirPath, fileName_Bookmarks_)); |
297 | if (open_File(f, writeOnly_FileMode | text_FileMode)) { | 390 | if (open_File(f, writeOnly_FileMode | text_FileMode)) { |
298 | iString *str = collectNew_String(); | 391 | iString *str = collectNew_String(); |
@@ -300,13 +393,12 @@ void save_Bookmarks(const iBookmarks *d, const char *dirPath) { | |||
300 | writeData_File(f, cstr_String(str), size_String(str)); | 393 | writeData_File(f, cstr_String(str), size_String(str)); |
301 | iConstForEach(Hash, i, &d->bookmarks) { | 394 | iConstForEach(Hash, i, &d->bookmarks) { |
302 | const iBookmark *bm = (const iBookmark *) i.value; | 395 | const iBookmark *bm = (const iBookmark *) i.value; |
303 | iRegExpMatch m; | 396 | if (bm->flags & remote_BookmarkFlag) { |
304 | init_RegExpMatch(&m); | ||
305 | if (matchString_RegExp(remotePattern, &bm->tags, &m)) { | ||
306 | /* Remote bookmarks are not saved. */ | 397 | /* Remote bookmarks are not saved. */ |
307 | continue; | 398 | continue; |
308 | } | 399 | } |
309 | iBeginCollect(); | 400 | iBeginCollect(); |
401 | const iString *packedTags = collect_String(packedDotTags_Bookmark_(bm)); | ||
310 | format_String(str, | 402 | format_String(str, |
311 | "[%d]\n" | 403 | "[%d]\n" |
312 | "url = \"%s\"\n" | 404 | "url = \"%s\"\n" |
@@ -317,7 +409,7 @@ void save_Bookmarks(const iBookmarks *d, const char *dirPath) { | |||
317 | id_Bookmark(bm), | 409 | id_Bookmark(bm), |
318 | cstrCollect_String(quote_String(&bm->url, iFalse)), | 410 | cstrCollect_String(quote_String(&bm->url, iFalse)), |
319 | cstrCollect_String(quote_String(&bm->title, iFalse)), | 411 | cstrCollect_String(quote_String(&bm->title, iFalse)), |
320 | cstrCollect_String(quote_String(&bm->tags, iFalse)), | 412 | cstrCollect_String(quote_String(packedTags, iFalse)), |
321 | bm->icon, | 413 | bm->icon, |
322 | seconds_Time(&bm->when), | 414 | seconds_Time(&bm->when), |
323 | cstrCollect_String(format_Time(&bm->when, "%Y-%m-%d"))); | 415 | cstrCollect_String(format_Time(&bm->when, "%Y-%m-%d"))); |
@@ -397,7 +489,7 @@ iBool updateBookmarkIcon_Bookmarks(iBookmarks *d, const iString *url, iChar icon | |||
397 | const uint32_t id = findUrl_Bookmarks(d, url); | 489 | const uint32_t id = findUrl_Bookmarks(d, url); |
398 | if (id) { | 490 | if (id) { |
399 | iBookmark *bm = get_Bookmarks(d, id); | 491 | iBookmark *bm = get_Bookmarks(d, id); |
400 | if (!hasTag_Bookmark(bm, remote_BookmarkTag) && !hasTag_Bookmark(bm, userIcon_BookmarkTag)) { | 492 | if (~bm->flags & remote_BookmarkFlag && ~bm->flags & userIcon_BookmarkFlag) { |
401 | if (icon != bm->icon) { | 493 | if (icon != bm->icon) { |
402 | bm->icon = icon; | 494 | bm->icon = icon; |
403 | changed = iTrue; | 495 | changed = iTrue; |
@@ -422,19 +514,13 @@ iChar siteIcon_Bookmarks(const iBookmarks *d, const iString *url) { | |||
422 | if (isEmpty_String(url)) { | 514 | if (isEmpty_String(url)) { |
423 | return 0; | 515 | return 0; |
424 | } | 516 | } |
425 | static iRegExp *tagPattern_; | ||
426 | if (!tagPattern_) { | ||
427 | tagPattern_ = new_RegExp("\\b" userIcon_BookmarkTag "\\b", caseSensitive_RegExpOption); | ||
428 | } | ||
429 | const iRangecc urlRoot = urlRoot_String(url); | 517 | const iRangecc urlRoot = urlRoot_String(url); |
430 | size_t matchingSize = iInvalidSize; /* we'll pick the shortest matching */ | 518 | size_t matchingSize = iInvalidSize; /* we'll pick the shortest matching */ |
431 | iChar icon = 0; | 519 | iChar icon = 0; |
432 | lock_Mutex(d->mtx); | 520 | lock_Mutex(d->mtx); |
433 | iConstForEach(Hash, i, &d->bookmarks) { | 521 | iConstForEach(Hash, i, &d->bookmarks) { |
434 | const iBookmark *bm = (const iBookmark *) i.value; | 522 | const iBookmark *bm = (const iBookmark *) i.value; |
435 | iRegExpMatch m; | 523 | if (bm->icon && bm->flags & userIcon_BookmarkFlag) { |
436 | init_RegExpMatch(&m); | ||
437 | if (bm->icon && matchString_RegExp(tagPattern_, &bm->tags, &m)) { | ||
438 | const iRangecc bmRoot = urlRoot_String(&bm->url); | 524 | const iRangecc bmRoot = urlRoot_String(&bm->url); |
439 | if (equalRangeCase_Rangecc(urlRoot, bmRoot)) { | 525 | if (equalRangeCase_Rangecc(urlRoot, bmRoot)) { |
440 | const size_t n = size_String(&bm->url); | 526 | const size_t n = size_String(&bm->url); |
@@ -467,10 +553,15 @@ void reorder_Bookmarks(iBookmarks *d, uint32_t id, int newOrder) { | |||
467 | unlock_Mutex(d->mtx); | 553 | unlock_Mutex(d->mtx); |
468 | } | 554 | } |
469 | 555 | ||
470 | iBool filterTagsRegExp_Bookmarks(void *regExp, const iBookmark *bm) { | 556 | //iBool filterTagsRegExp_Bookmarks(void *regExp, const iBookmark *bm) { |
471 | iRegExpMatch m; | 557 | // iRegExpMatch m; |
472 | init_RegExpMatch(&m); | 558 | // init_RegExpMatch(&m); |
473 | return matchString_RegExp(regExp, &bm->tags, &m); | 559 | // return matchString_RegExp(regExp, &bm->tags, &m); |
560 | //} | ||
561 | |||
562 | iBool filterHomepage_Bookmark(void *d, const iBookmark *bm) { | ||
563 | iUnused(d); | ||
564 | return (bm->flags & homepage_BookmarkFlag) != 0; | ||
474 | } | 565 | } |
475 | 566 | ||
476 | static iBool matchUrl_(void *url, const iBookmark *bm) { | 567 | static iBool matchUrl_(void *url, const iBookmark *bm) { |
@@ -618,7 +709,7 @@ const iString *bookmarkListPage_Bookmarks(const iBookmarks *d, enum iBookmarkLis | |||
618 | 709 | ||
619 | static iBool isRemoteSource_Bookmark_(void *context, const iBookmark *d) { | 710 | static iBool isRemoteSource_Bookmark_(void *context, const iBookmark *d) { |
620 | iUnused(context); | 711 | iUnused(context); |
621 | return hasTag_Bookmark(d, remoteSource_BookmarkTag); | 712 | return (d->flags & remoteSource_BookmarkFlag) != 0; |
622 | } | 713 | } |
623 | 714 | ||
624 | void remoteRequestFinished_Bookmarks_(iBookmarks *d, iGmRequest *req) { | 715 | void remoteRequestFinished_Bookmarks_(iBookmarks *d, iGmRequest *req) { |
@@ -642,7 +733,6 @@ void requestFinished_Bookmarks(iBookmarks *d, iGmRequest *req) { | |||
642 | initCurrent_Time(&now); | 733 | initCurrent_Time(&now); |
643 | iRegExp *linkPattern = new_RegExp("^=>\\s*([^\\s]+)(\\s+(.*))?", 0); | 734 | iRegExp *linkPattern = new_RegExp("^=>\\s*([^\\s]+)(\\s+(.*))?", 0); |
644 | iString src; | 735 | iString src; |
645 | const iString *remoteTag = collectNewCStr_String("remote"); | ||
646 | initBlock_String(&src, body_GmRequest(req)); | 736 | initBlock_String(&src, body_GmRequest(req)); |
647 | iRangecc srcLine = iNullRange; | 737 | iRangecc srcLine = iNullRange; |
648 | while (nextSplit_Rangecc(range_String(&src), "\n", &srcLine)) { | 738 | while (nextSplit_Rangecc(range_String(&src), "\n", &srcLine)) { |
@@ -660,8 +750,9 @@ void requestFinished_Bookmarks(iBookmarks *d, iGmRequest *req) { | |||
660 | if (isEmpty_String(titleStr)) { | 750 | if (isEmpty_String(titleStr)) { |
661 | setRange_String(titleStr, urlHost_String(urlStr)); | 751 | setRange_String(titleStr, urlHost_String(urlStr)); |
662 | } | 752 | } |
663 | const uint32_t bmId = add_Bookmarks(d, absUrl, titleStr, remoteTag, 0x2913); | 753 | const uint32_t bmId = add_Bookmarks(d, absUrl, titleStr, NULL, 0x2913); |
664 | iBookmark *bm = get_Bookmarks(d, bmId); | 754 | iBookmark *bm = get_Bookmarks(d, bmId); |
755 | bm->flags |= remote_BookmarkFlag; | ||
665 | bm->parentId = *(uint32_t *) userData_Object(req); | 756 | bm->parentId = *(uint32_t *) userData_Object(req); |
666 | delete_String(titleStr); | 757 | delete_String(titleStr); |
667 | } | 758 | } |
@@ -690,7 +781,7 @@ void fetchRemote_Bookmarks(iBookmarks *d) { | |||
690 | size_t numRemoved = 0; | 781 | size_t numRemoved = 0; |
691 | iForEach(Hash, i, &d->bookmarks) { | 782 | iForEach(Hash, i, &d->bookmarks) { |
692 | iBookmark *bm = (iBookmark *) i.value; | 783 | iBookmark *bm = (iBookmark *) i.value; |
693 | if (hasTag_Bookmark(bm, remote_BookmarkTag)) { | 784 | if (bm->flags & remote_BookmarkFlag) { |
694 | remove_HashIterator(&i); | 785 | remove_HashIterator(&i); |
695 | delete_Bookmark(bm); | 786 | delete_Bookmark(bm); |
696 | numRemoved++; | 787 | numRemoved++; |