Add initial code syntax highlighting.

Thanks PrismJS and Fela for porting it to C++.
This commit is contained in:
John Preston 2023-10-03 18:22:59 +04:00
parent da768ac1d1
commit 2414e927bd
15 changed files with 112 additions and 5 deletions

View File

@ -1540,6 +1540,7 @@ elseif (APPLE)
PRE_LINK
COMMAND mkdir -p $<TARGET_FILE_DIR:Telegram>/../Resources
COMMAND cp ${CMAKE_BINARY_DIR}/lib_ui.rcc $<TARGET_FILE_DIR:Telegram>/../Resources
COMMAND cp ${CMAKE_BINARY_DIR}/lib_spellcheck.rcc $<TARGET_FILE_DIR:Telegram>/../Resources
)
if (NOT build_macstore)
add_custom_command(TARGET Telegram

View File

@ -80,6 +80,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "base/call_delayed.h"
#include "base/random.h"
#include "spellcheck/spellcheck_highlight_syntax.h"
#include "styles/style_boxes.h" // st::backgroundSize
namespace Data {
@ -300,6 +301,11 @@ Session::Session(not_null<Main::Session*> session)
}
}, _lifetime);
Spellchecker::HighlightReady(
) | rpl::start_with_next([=](uint64 processId) {
highlightProcessDone(processId);
}, _lifetime);
subscribeForTopicRepliesLists();
crl::on_main(_session, [=] {
@ -1760,6 +1766,27 @@ void Session::requestItemTextRefresh(not_null<HistoryItem*> item) {
}
}
void Session::registerHighlightProcess(
uint64 processId,
not_null<HistoryItem*> item) {
Expects(item->inHighlightProcess());
const auto [i, ok] = _highlightings.emplace(processId, item);
Ensures(ok);
}
void Session::highlightProcessDone(uint64 processId) {
if (const auto done = _highlightings.take(processId)) {
for (const auto &[id, item] : _highlightings) {
if (item == *done) {
return;
}
}
(*done)->highlightProcessDone();
}
}
void Session::requestUnreadReactionsAnimation(not_null<HistoryItem*> item) {
enumerateItemViews(item, [&](not_null<ViewElement*> view) {
view->animateUnreadReactions();
@ -2415,6 +2442,13 @@ void Session::unregisterMessage(not_null<HistoryItem*> item) {
Data::MessageUpdate::Flag::Destroyed);
groups().unregisterMessage(item);
removeDependencyMessage(item);
for (auto i = begin(_highlightings); i != end(_highlightings);) {
if (i->second == item) {
i = _highlightings.erase(i);
} else {
++i;
}
}
messagesListForInsert(peerId)->erase(itemId);
if (!peerIsChannel(peerId) && IsServerMsgId(itemId)) {

View File

@ -299,6 +299,10 @@ public:
void notifyPinnedDialogsOrderUpdated();
[[nodiscard]] rpl::producer<> pinnedDialogsOrderUpdated() const;
void registerHighlightProcess(
uint64 processId,
not_null<HistoryItem*> item);
void registerHeavyViewPart(not_null<ViewElement*> view);
void unregisterHeavyViewPart(not_null<ViewElement*> view);
void unloadHeavyViewParts(
@ -845,6 +849,7 @@ private:
TimeId date);
void setWallpapers(const QVector<MTPWallPaper> &data, uint64 hash);
void highlightProcessDone(uint64 processId);
void checkPollsClosings();
@ -955,6 +960,7 @@ private:
std::unordered_map<
FullStoryId,
base::flat_set<not_null<HistoryItem*>>> _storyItems;
base::flat_map<uint64, not_null<HistoryItem*>> _highlightings;
base::flat_set<not_null<WebPageData*>> _webpagesUpdated;
base::flat_set<not_null<GameData*>> _gamesUpdated;

View File

@ -303,6 +303,8 @@ enum class MessageFlag : uint64 {
FakeBotAbout = (1ULL << 36),
StoryItem = (1ULL << 37),
InHighlightProcess = (1ULL << 38),
};
inline constexpr bool is_flag_type(MessageFlag) { return true; }
using MessageFlags = base::flags<MessageFlag>;

View File

@ -67,6 +67,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_web_page.h"
#include "chat_helpers/stickers_gift_box_pack.h"
#include "payments/payments_checkout_process.h" // CheckoutProcess::Start.
#include "spellcheck/spellcheck_highlight_syntax.h"
#include "styles/style_dialogs.h"
namespace {
@ -2871,15 +2872,32 @@ void HistoryItem::setText(const TextWithEntities &textWithEntities) {
: std::move(textWithEntities));
}
void HistoryItem::setTextValue(TextWithEntities text) {
void HistoryItem::setTextValue(TextWithEntities text, bool force) {
if (const auto processId = Spellchecker::TryHighlightSyntax(text)) {
_flags |= MessageFlag::InHighlightProcess;
history()->owner().registerHighlightProcess(processId, this);
}
const auto had = !_text.empty();
_text = std::move(text);
RemoveComponents(HistoryMessageTranslation::Bit());
if (had) {
if (had || force) {
history()->owner().requestItemTextRefresh(this);
}
}
bool HistoryItem::inHighlightProcess() const {
return _flags & MessageFlag::InHighlightProcess;
}
void HistoryItem::highlightProcessDone() {
Expects(inHighlightProcess());
_flags &= ~MessageFlag::InHighlightProcess;
if (!_text.empty()) {
setTextValue(base::take(_text), true);
}
}
bool HistoryItem::showNotification() const {
const auto channel = _history->peer->asChannel();
if (channel && !channel->amIn()) {

View File

@ -322,6 +322,8 @@ public:
[[nodiscard]] bool repliesAreComments() const;
[[nodiscard]] bool externalReply() const;
[[nodiscard]] bool hasExtendedMediaPreview() const;
[[nodiscard]] bool inHighlightProcess() const;
void highlightProcessDone();
void setCommentsInboxReadTill(MsgId readTillId);
void setCommentsMaxId(MsgId maxId);
@ -537,7 +539,7 @@ private:
[[nodiscard]] bool generateLocalEntitiesByReply() const;
[[nodiscard]] TextWithEntities withLocalEntities(
const TextWithEntities &textWithEntities) const;
void setTextValue(TextWithEntities text);
void setTextValue(TextWithEntities text, bool force = false);
[[nodiscard]] bool isTooOldForEdit(TimeId now) const;
[[nodiscard]] bool isLegacyMessage() const {
return _flags & MessageFlag::Legacy;

View File

@ -1622,6 +1622,7 @@ void Message::paintText(
.position = trect.topLeft(),
.availableWidth = trect.width(),
.palette = &stm->textPalette,
.colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now,
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),

View File

@ -748,6 +748,7 @@ void Document::draw(
.position = { st::msgPadding.left(), captiontop },
.availableWidth = captionw,
.palette = &stm->textPalette,
.colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now,
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),

View File

@ -235,6 +235,7 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
painty + painth + st::mediaCaptionSkip),
.availableWidth = captionw,
.palette = &stm->textPalette,
.colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now,
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),

View File

@ -709,6 +709,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
.position = QPoint(st::msgPadding.left(), top),
.availableWidth = captionw,
.palette = &stm->textPalette,
.colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now,
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),

View File

@ -405,6 +405,7 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
.position = QPoint(st::msgPadding.left(), top),
.availableWidth = captionw,
.palette = &stm->textPalette,
.colors = context.st->highlightColors(),
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now,
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),

View File

@ -446,6 +446,41 @@ void ChatStyle::applyAdjustedServiceBg(QColor serviceBg) {
msgServiceBg().set(uchar(r), uchar(g), uchar(b), uchar(a));
}
std::span<Ui::Text::SpecialColor> ChatStyle::highlightColors() const {
if (_highlightColors.empty()) {
const auto push = [&](const style::color &color) {
_highlightColors.push_back({ &color->p, &color->p });
};
// comment, block-comment, prolog, doctype, cdata
push(statisticsChartLineLightblue());
// punctuation
push(statisticsChartLineRed());
// property, tag, boolean, number,
// constant, symbol, deleted
push(statisticsChartLineRed());
// selector, attr-name, string, char, builtin, inserted
push(statisticsChartLineOrange());
// operator, entity, url
push(statisticsChartLineRed());
// atrule, attr-value, keyword, function
push(statisticsChartLineBlue());
// class-name
push(statisticsChartLinePurple());
//push(statisticsChartLineLightgreen());
//push(statisticsChartLineGreen());
//push(statisticsChartLineGolden());
}
return _highlightColors;
}
void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
*static_cast<style::palette*>(this) = *palette;
style::internal::resetIcons();

View File

@ -168,6 +168,8 @@ public:
void applyCustomPalette(const style::palette *palette);
void applyAdjustedServiceBg(QColor serviceBg);
[[nodiscard]] std::span<Ui::Text::SpecialColor> highlightColors() const;
[[nodiscard]] rpl::producer<> paletteChanged() const {
return _paletteChanged.events();
}
@ -332,6 +334,8 @@ private:
mutable CornersPixmaps _msgSelectOverlayCorners[
int(CachedCornerRadius::kCount)];
mutable std::vector<Ui::Text::SpecialColor> _highlightColors;
style::TextPalette _historyPsaForwardPalette;
style::TextPalette _imgReplyTextPalette;
style::TextPalette _serviceTextPalette;

@ -1 +1 @@
Subproject commit ab2a9d1c19b948a05b216801f8ba4fea0759428a
Subproject commit a9fb0310132bc645ffcfb3734bd2ca53f1fb39fa

@ -1 +1 @@
Subproject commit 02440524eaa2b1bd6d1909ff4aa2ca207a282b2c
Subproject commit 0d8717d48ac3f2228399a1de78a28f0a7bbd0023