Add initial code syntax highlighting.
Thanks PrismJS and Fela for porting it to C++.
This commit is contained in:
parent
da768ac1d1
commit
2414e927bd
|
@ -1540,6 +1540,7 @@ elseif (APPLE)
|
||||||
PRE_LINK
|
PRE_LINK
|
||||||
COMMAND mkdir -p $<TARGET_FILE_DIR:Telegram>/../Resources
|
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_ui.rcc $<TARGET_FILE_DIR:Telegram>/../Resources
|
||||||
|
COMMAND cp ${CMAKE_BINARY_DIR}/lib_spellcheck.rcc $<TARGET_FILE_DIR:Telegram>/../Resources
|
||||||
)
|
)
|
||||||
if (NOT build_macstore)
|
if (NOT build_macstore)
|
||||||
add_custom_command(TARGET Telegram
|
add_custom_command(TARGET Telegram
|
||||||
|
|
|
@ -80,6 +80,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "base/call_delayed.h"
|
#include "base/call_delayed.h"
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
|
#include "spellcheck/spellcheck_highlight_syntax.h"
|
||||||
#include "styles/style_boxes.h" // st::backgroundSize
|
#include "styles/style_boxes.h" // st::backgroundSize
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
@ -300,6 +301,11 @@ Session::Session(not_null<Main::Session*> session)
|
||||||
}
|
}
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
|
Spellchecker::HighlightReady(
|
||||||
|
) | rpl::start_with_next([=](uint64 processId) {
|
||||||
|
highlightProcessDone(processId);
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
subscribeForTopicRepliesLists();
|
subscribeForTopicRepliesLists();
|
||||||
|
|
||||||
crl::on_main(_session, [=] {
|
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) {
|
void Session::requestUnreadReactionsAnimation(not_null<HistoryItem*> item) {
|
||||||
enumerateItemViews(item, [&](not_null<ViewElement*> view) {
|
enumerateItemViews(item, [&](not_null<ViewElement*> view) {
|
||||||
view->animateUnreadReactions();
|
view->animateUnreadReactions();
|
||||||
|
@ -2415,6 +2442,13 @@ void Session::unregisterMessage(not_null<HistoryItem*> item) {
|
||||||
Data::MessageUpdate::Flag::Destroyed);
|
Data::MessageUpdate::Flag::Destroyed);
|
||||||
groups().unregisterMessage(item);
|
groups().unregisterMessage(item);
|
||||||
removeDependencyMessage(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);
|
messagesListForInsert(peerId)->erase(itemId);
|
||||||
|
|
||||||
if (!peerIsChannel(peerId) && IsServerMsgId(itemId)) {
|
if (!peerIsChannel(peerId) && IsServerMsgId(itemId)) {
|
||||||
|
|
|
@ -299,6 +299,10 @@ public:
|
||||||
void notifyPinnedDialogsOrderUpdated();
|
void notifyPinnedDialogsOrderUpdated();
|
||||||
[[nodiscard]] rpl::producer<> pinnedDialogsOrderUpdated() const;
|
[[nodiscard]] rpl::producer<> pinnedDialogsOrderUpdated() const;
|
||||||
|
|
||||||
|
void registerHighlightProcess(
|
||||||
|
uint64 processId,
|
||||||
|
not_null<HistoryItem*> item);
|
||||||
|
|
||||||
void registerHeavyViewPart(not_null<ViewElement*> view);
|
void registerHeavyViewPart(not_null<ViewElement*> view);
|
||||||
void unregisterHeavyViewPart(not_null<ViewElement*> view);
|
void unregisterHeavyViewPart(not_null<ViewElement*> view);
|
||||||
void unloadHeavyViewParts(
|
void unloadHeavyViewParts(
|
||||||
|
@ -845,6 +849,7 @@ private:
|
||||||
TimeId date);
|
TimeId date);
|
||||||
|
|
||||||
void setWallpapers(const QVector<MTPWallPaper> &data, uint64 hash);
|
void setWallpapers(const QVector<MTPWallPaper> &data, uint64 hash);
|
||||||
|
void highlightProcessDone(uint64 processId);
|
||||||
|
|
||||||
void checkPollsClosings();
|
void checkPollsClosings();
|
||||||
|
|
||||||
|
@ -955,6 +960,7 @@ private:
|
||||||
std::unordered_map<
|
std::unordered_map<
|
||||||
FullStoryId,
|
FullStoryId,
|
||||||
base::flat_set<not_null<HistoryItem*>>> _storyItems;
|
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<WebPageData*>> _webpagesUpdated;
|
||||||
base::flat_set<not_null<GameData*>> _gamesUpdated;
|
base::flat_set<not_null<GameData*>> _gamesUpdated;
|
||||||
|
|
|
@ -303,6 +303,8 @@ enum class MessageFlag : uint64 {
|
||||||
FakeBotAbout = (1ULL << 36),
|
FakeBotAbout = (1ULL << 36),
|
||||||
|
|
||||||
StoryItem = (1ULL << 37),
|
StoryItem = (1ULL << 37),
|
||||||
|
|
||||||
|
InHighlightProcess = (1ULL << 38),
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||||
using MessageFlags = base::flags<MessageFlag>;
|
using MessageFlags = base::flags<MessageFlag>;
|
||||||
|
|
|
@ -67,6 +67,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_web_page.h"
|
#include "data/data_web_page.h"
|
||||||
#include "chat_helpers/stickers_gift_box_pack.h"
|
#include "chat_helpers/stickers_gift_box_pack.h"
|
||||||
#include "payments/payments_checkout_process.h" // CheckoutProcess::Start.
|
#include "payments/payments_checkout_process.h" // CheckoutProcess::Start.
|
||||||
|
#include "spellcheck/spellcheck_highlight_syntax.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -2871,15 +2872,32 @@ void HistoryItem::setText(const TextWithEntities &textWithEntities) {
|
||||||
: std::move(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();
|
const auto had = !_text.empty();
|
||||||
_text = std::move(text);
|
_text = std::move(text);
|
||||||
RemoveComponents(HistoryMessageTranslation::Bit());
|
RemoveComponents(HistoryMessageTranslation::Bit());
|
||||||
if (had) {
|
if (had || force) {
|
||||||
history()->owner().requestItemTextRefresh(this);
|
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 {
|
bool HistoryItem::showNotification() const {
|
||||||
const auto channel = _history->peer->asChannel();
|
const auto channel = _history->peer->asChannel();
|
||||||
if (channel && !channel->amIn()) {
|
if (channel && !channel->amIn()) {
|
||||||
|
|
|
@ -322,6 +322,8 @@ public:
|
||||||
[[nodiscard]] bool repliesAreComments() const;
|
[[nodiscard]] bool repliesAreComments() const;
|
||||||
[[nodiscard]] bool externalReply() const;
|
[[nodiscard]] bool externalReply() const;
|
||||||
[[nodiscard]] bool hasExtendedMediaPreview() const;
|
[[nodiscard]] bool hasExtendedMediaPreview() const;
|
||||||
|
[[nodiscard]] bool inHighlightProcess() const;
|
||||||
|
void highlightProcessDone();
|
||||||
|
|
||||||
void setCommentsInboxReadTill(MsgId readTillId);
|
void setCommentsInboxReadTill(MsgId readTillId);
|
||||||
void setCommentsMaxId(MsgId maxId);
|
void setCommentsMaxId(MsgId maxId);
|
||||||
|
@ -537,7 +539,7 @@ private:
|
||||||
[[nodiscard]] bool generateLocalEntitiesByReply() const;
|
[[nodiscard]] bool generateLocalEntitiesByReply() const;
|
||||||
[[nodiscard]] TextWithEntities withLocalEntities(
|
[[nodiscard]] TextWithEntities withLocalEntities(
|
||||||
const TextWithEntities &textWithEntities) const;
|
const TextWithEntities &textWithEntities) const;
|
||||||
void setTextValue(TextWithEntities text);
|
void setTextValue(TextWithEntities text, bool force = false);
|
||||||
[[nodiscard]] bool isTooOldForEdit(TimeId now) const;
|
[[nodiscard]] bool isTooOldForEdit(TimeId now) const;
|
||||||
[[nodiscard]] bool isLegacyMessage() const {
|
[[nodiscard]] bool isLegacyMessage() const {
|
||||||
return _flags & MessageFlag::Legacy;
|
return _flags & MessageFlag::Legacy;
|
||||||
|
|
|
@ -1622,6 +1622,7 @@ void Message::paintText(
|
||||||
.position = trect.topLeft(),
|
.position = trect.topLeft(),
|
||||||
.availableWidth = trect.width(),
|
.availableWidth = trect.width(),
|
||||||
.palette = &stm->textPalette,
|
.palette = &stm->textPalette,
|
||||||
|
.colors = context.st->highlightColors(),
|
||||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||||
.now = context.now,
|
.now = context.now,
|
||||||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||||
|
|
|
@ -748,6 +748,7 @@ void Document::draw(
|
||||||
.position = { st::msgPadding.left(), captiontop },
|
.position = { st::msgPadding.left(), captiontop },
|
||||||
.availableWidth = captionw,
|
.availableWidth = captionw,
|
||||||
.palette = &stm->textPalette,
|
.palette = &stm->textPalette,
|
||||||
|
.colors = context.st->highlightColors(),
|
||||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||||
.now = context.now,
|
.now = context.now,
|
||||||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||||
|
|
|
@ -235,6 +235,7 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const {
|
||||||
painty + painth + st::mediaCaptionSkip),
|
painty + painth + st::mediaCaptionSkip),
|
||||||
.availableWidth = captionw,
|
.availableWidth = captionw,
|
||||||
.palette = &stm->textPalette,
|
.palette = &stm->textPalette,
|
||||||
|
.colors = context.st->highlightColors(),
|
||||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||||
.now = context.now,
|
.now = context.now,
|
||||||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||||
|
|
|
@ -709,6 +709,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
||||||
.position = QPoint(st::msgPadding.left(), top),
|
.position = QPoint(st::msgPadding.left(), top),
|
||||||
.availableWidth = captionw,
|
.availableWidth = captionw,
|
||||||
.palette = &stm->textPalette,
|
.palette = &stm->textPalette,
|
||||||
|
.colors = context.st->highlightColors(),
|
||||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||||
.now = context.now,
|
.now = context.now,
|
||||||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||||
|
|
|
@ -405,6 +405,7 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
|
||||||
.position = QPoint(st::msgPadding.left(), top),
|
.position = QPoint(st::msgPadding.left(), top),
|
||||||
.availableWidth = captionw,
|
.availableWidth = captionw,
|
||||||
.palette = &stm->textPalette,
|
.palette = &stm->textPalette,
|
||||||
|
.colors = context.st->highlightColors(),
|
||||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||||
.now = context.now,
|
.now = context.now,
|
||||||
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||||
|
|
|
@ -446,6 +446,41 @@ void ChatStyle::applyAdjustedServiceBg(QColor serviceBg) {
|
||||||
msgServiceBg().set(uchar(r), uchar(g), uchar(b), uchar(a));
|
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) {
|
void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
|
||||||
*static_cast<style::palette*>(this) = *palette;
|
*static_cast<style::palette*>(this) = *palette;
|
||||||
style::internal::resetIcons();
|
style::internal::resetIcons();
|
||||||
|
|
|
@ -168,6 +168,8 @@ public:
|
||||||
void applyCustomPalette(const style::palette *palette);
|
void applyCustomPalette(const style::palette *palette);
|
||||||
void applyAdjustedServiceBg(QColor serviceBg);
|
void applyAdjustedServiceBg(QColor serviceBg);
|
||||||
|
|
||||||
|
[[nodiscard]] std::span<Ui::Text::SpecialColor> highlightColors() const;
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<> paletteChanged() const {
|
[[nodiscard]] rpl::producer<> paletteChanged() const {
|
||||||
return _paletteChanged.events();
|
return _paletteChanged.events();
|
||||||
}
|
}
|
||||||
|
@ -332,6 +334,8 @@ private:
|
||||||
mutable CornersPixmaps _msgSelectOverlayCorners[
|
mutable CornersPixmaps _msgSelectOverlayCorners[
|
||||||
int(CachedCornerRadius::kCount)];
|
int(CachedCornerRadius::kCount)];
|
||||||
|
|
||||||
|
mutable std::vector<Ui::Text::SpecialColor> _highlightColors;
|
||||||
|
|
||||||
style::TextPalette _historyPsaForwardPalette;
|
style::TextPalette _historyPsaForwardPalette;
|
||||||
style::TextPalette _imgReplyTextPalette;
|
style::TextPalette _imgReplyTextPalette;
|
||||||
style::TextPalette _serviceTextPalette;
|
style::TextPalette _serviceTextPalette;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit ab2a9d1c19b948a05b216801f8ba4fea0759428a
|
Subproject commit a9fb0310132bc645ffcfb3734bd2ca53f1fb39fa
|
|
@ -1 +1 @@
|
||||||
Subproject commit 02440524eaa2b1bd6d1909ff4aa2ca207a282b2c
|
Subproject commit 0d8717d48ac3f2228399a1de78a28f0a7bbd0023
|
Loading…
Reference in New Issue
Block a user