Allow sending pre-blocks with language.
This commit is contained in:
parent
02440524ea
commit
0d8717d48a
2 changed files with 61 additions and 20 deletions
|
|
@ -2032,6 +2032,7 @@ EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags) {
|
||||||
};
|
};
|
||||||
struct State {
|
struct State {
|
||||||
QString link;
|
QString link;
|
||||||
|
QString language;
|
||||||
uint32 mask = 0;
|
uint32 mask = 0;
|
||||||
|
|
||||||
void set(EntityType type) {
|
void set(EntityType type) {
|
||||||
|
|
@ -2118,28 +2119,34 @@ EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags) {
|
||||||
}
|
}
|
||||||
for (const auto type : kInMaskTypes) {
|
for (const auto type : kInMaskTypes) {
|
||||||
if (nextState.has(type) && !state.has(type)) {
|
if (nextState.has(type) && !state.has(type)) {
|
||||||
openType(type);
|
openType(type, nextState.language);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state = nextState;
|
state = nextState;
|
||||||
};
|
};
|
||||||
const auto stateForTag = [&](const QString &tag) {
|
const auto stateForTag = [&](const QString &tag) {
|
||||||
|
using Tags = Ui::InputField;
|
||||||
auto result = State();
|
auto result = State();
|
||||||
const auto list = SplitTags(tag);
|
const auto list = SplitTags(tag);
|
||||||
|
const auto languageStart = Tags::kTagPre.size();
|
||||||
for (const auto &single : list) {
|
for (const auto &single : list) {
|
||||||
if (single == Ui::InputField::kTagBold) {
|
if (single == Tags::kTagBold) {
|
||||||
result.set(EntityType::Bold);
|
result.set(EntityType::Bold);
|
||||||
} else if (single == Ui::InputField::kTagItalic) {
|
} else if (single == Tags::kTagItalic) {
|
||||||
result.set(EntityType::Italic);
|
result.set(EntityType::Italic);
|
||||||
} else if (single == Ui::InputField::kTagUnderline) {
|
} else if (single == Tags::kTagUnderline) {
|
||||||
result.set(EntityType::Underline);
|
result.set(EntityType::Underline);
|
||||||
} else if (single == Ui::InputField::kTagStrikeOut) {
|
} else if (single == Tags::kTagStrikeOut) {
|
||||||
result.set(EntityType::StrikeOut);
|
result.set(EntityType::StrikeOut);
|
||||||
} else if (single == Ui::InputField::kTagCode) {
|
} else if (single == Tags::kTagCode) {
|
||||||
result.set(EntityType::Code);
|
result.set(EntityType::Code);
|
||||||
} else if (single == Ui::InputField::kTagPre) {
|
} else if (single == Tags::kTagPre) {
|
||||||
result.set(EntityType::Pre);
|
result.set(EntityType::Pre);
|
||||||
} else if (single == Ui::InputField::kTagSpoiler) {
|
} else if (single.size() > languageStart
|
||||||
|
&& single.startsWith(Tags::kTagPre)) {
|
||||||
|
result.set(EntityType::Pre);
|
||||||
|
result.language = single.mid(languageStart).toString();
|
||||||
|
} else if (single == Tags::kTagSpoiler) {
|
||||||
result.set(EntityType::Spoiler);
|
result.set(EntityType::Spoiler);
|
||||||
} else {
|
} else {
|
||||||
result.link = single.toString();
|
result.link = single.toString();
|
||||||
|
|
@ -2235,8 +2242,17 @@ TextWithTags::Tags ConvertEntitiesToTextTags(
|
||||||
case EntityType::StrikeOut:
|
case EntityType::StrikeOut:
|
||||||
push(Ui::InputField::kTagStrikeOut);
|
push(Ui::InputField::kTagStrikeOut);
|
||||||
break;
|
break;
|
||||||
case EntityType::Code: push(Ui::InputField::kTagCode); break; // #TODO entities
|
case EntityType::Code: push(Ui::InputField::kTagCode); break;
|
||||||
case EntityType::Pre: push(Ui::InputField::kTagPre); break;
|
case EntityType::Pre: {
|
||||||
|
if (!entity.data().isEmpty()) {
|
||||||
|
const auto language = QRegularExpression("^[a-z0-9\\-]+$");
|
||||||
|
if (language.match(entity.data()).hasMatch()) {
|
||||||
|
push(Ui::InputField::kTagPre + entity.data());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
push(Ui::InputField::kTagPre);
|
||||||
|
} break;
|
||||||
case EntityType::Spoiler: push(Ui::InputField::kTagSpoiler); break;
|
case EntityType::Spoiler: push(Ui::InputField::kTagSpoiler); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,10 @@ const auto kNewlineChars = QString("\r\n")
|
||||||
// QTextCharFormat with the same properties, including kCustomEmojiText.
|
// QTextCharFormat with the same properties, including kCustomEmojiText.
|
||||||
auto GlobalCustomEmojiCounter = 0;
|
auto GlobalCustomEmojiCounter = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsTagPre(QStringView tag) {
|
||||||
|
return tag.startsWith(kTagPre);
|
||||||
|
}
|
||||||
|
|
||||||
class InputDocument : public QTextDocument {
|
class InputDocument : public QTextDocument {
|
||||||
public:
|
public:
|
||||||
InputDocument(QObject *parent, const style::InputField &st);
|
InputDocument(QObject *parent, const style::InputField &st);
|
||||||
|
|
@ -169,7 +173,7 @@ bool IsNewline(QChar ch) {
|
||||||
}
|
}
|
||||||
auto found = false;
|
auto found = false;
|
||||||
for (const auto &single : TextUtilities::SplitTags(existing.id)) {
|
for (const auto &single : TextUtilities::SplitTags(existing.id)) {
|
||||||
const auto normalized = (single == QStringView(kTagPre))
|
const auto normalized = IsTagPre(single)
|
||||||
? QStringView(kTagCode)
|
? QStringView(kTagCode)
|
||||||
: single;
|
: single;
|
||||||
if (checkingLink && IsValidMarkdownLink(single)) {
|
if (checkingLink && IsValidMarkdownLink(single)) {
|
||||||
|
|
@ -207,6 +211,17 @@ bool IsNewline(QChar ch) {
|
||||||
return !CheckFullTextTag(textWithTags, tag).isEmpty();
|
return !CheckFullTextTag(textWithTags, tag).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QString ReadPreLanguageName(
|
||||||
|
const QString &text,
|
||||||
|
int preStart,
|
||||||
|
int preLength) {
|
||||||
|
auto view = QStringView(text).mid(preStart, preLength);
|
||||||
|
static const auto expression = QRegularExpression(
|
||||||
|
"^([a-zA-Z0-9\\-]+)[\\r\\n]");
|
||||||
|
const auto m = expression.match(view);
|
||||||
|
return m.hasMatch() ? m.captured(1).toLower() : QString();
|
||||||
|
}
|
||||||
|
|
||||||
class TagAccumulator {
|
class TagAccumulator {
|
||||||
public:
|
public:
|
||||||
TagAccumulator(TextWithTags::Tags &tags) : _tags(tags) {
|
TagAccumulator(TextWithTags::Tags &tags) : _tags(tags) {
|
||||||
|
|
@ -739,7 +754,7 @@ QTextCharFormat PrepareTagFormat(
|
||||||
font = font->underline();
|
font = font->underline();
|
||||||
} else if (tag == kTagStrikeOut) {
|
} else if (tag == kTagStrikeOut) {
|
||||||
font = font->strikeout();
|
font = font->strikeout();
|
||||||
} else if (tag == kTagCode || tag == kTagPre) {
|
} else if (tag == kTagCode || IsTagPre(tag)) {
|
||||||
color = st::defaultTextPalette.monoFg;
|
color = st::defaultTextPalette.monoFg;
|
||||||
font = font->monospace();
|
font = font->monospace();
|
||||||
} else if (tag == kTagSpoiler) {
|
} else if (tag == kTagSpoiler) {
|
||||||
|
|
@ -2668,11 +2683,21 @@ TextWithTags InputField::getTextWithAppliedMarkdown() const {
|
||||||
}
|
}
|
||||||
addOriginalTextUpTill(tag.adjustedStart);
|
addOriginalTextUpTill(tag.adjustedStart);
|
||||||
|
|
||||||
|
auto tagId = tag.tag;
|
||||||
auto entityStart = tag.adjustedStart + tagLength;
|
auto entityStart = tag.adjustedStart + tagLength;
|
||||||
if (tag.tag == kTagPre) {
|
if (tagId == kTagPre) {
|
||||||
// Remove redundant newlines for pre.
|
// Remove redundant newlines for pre.
|
||||||
// If ``` is on a separate line add only one newline.
|
// If ``` is on a separate line add only one newline.
|
||||||
if (IsNewline(originalText[entityStart])
|
const auto languageName = ReadPreLanguageName(
|
||||||
|
originalText,
|
||||||
|
entityStart,
|
||||||
|
entityLength);
|
||||||
|
if (!languageName.isEmpty()) {
|
||||||
|
// ```language-name{\n}code
|
||||||
|
entityStart += languageName.size() + 1;
|
||||||
|
entityLength -= languageName.size() + 1;
|
||||||
|
tagId += languageName;
|
||||||
|
} else if (IsNewline(originalText[entityStart])
|
||||||
&& (result.text.isEmpty()
|
&& (result.text.isEmpty()
|
||||||
|| IsNewline(result.text[result.text.size() - 1]))) {
|
|| IsNewline(result.text[result.text.size() - 1]))) {
|
||||||
++entityStart;
|
++entityStart;
|
||||||
|
|
@ -2691,7 +2716,7 @@ TextWithTags InputField::getTextWithAppliedMarkdown() const {
|
||||||
result.tags.push_back(TextWithTags::Tag{
|
result.tags.push_back(TextWithTags::Tag{
|
||||||
int(result.text.size()),
|
int(result.text.size()),
|
||||||
entityLength,
|
entityLength,
|
||||||
tag.tag });
|
tagId });
|
||||||
result.text.append(base::StringViewMid(
|
result.text.append(base::StringViewMid(
|
||||||
originalText,
|
originalText,
|
||||||
entityStart,
|
entityStart,
|
||||||
|
|
@ -3121,7 +3146,7 @@ void InputField::processInstantReplaces(const QString &appended) {
|
||||||
for (const auto &tag : _lastMarkdownTags) {
|
for (const auto &tag : _lastMarkdownTags) {
|
||||||
if (tag.internalStart < position
|
if (tag.internalStart < position
|
||||||
&& tag.internalStart + tag.internalLength >= position
|
&& tag.internalStart + tag.internalLength >= position
|
||||||
&& (tag.tag == kTagCode || tag.tag == kTagPre)) {
|
&& (tag.tag == kTagCode || IsTagPre(tag.tag))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3197,10 +3222,10 @@ void InputField::commitInstantReplacement(
|
||||||
const auto currentTag = cursor.charFormat().property(
|
const auto currentTag = cursor.charFormat().property(
|
||||||
kTagProperty
|
kTagProperty
|
||||||
).toString();
|
).toString();
|
||||||
const auto currentTags = TextUtilities::SplitTags(currentTag);
|
for (const auto &tag : TextUtilities::SplitTags(currentTag)) {
|
||||||
if (currentTags.contains(QStringView(kTagPre))
|
if (tag == kTagCode || IsTagPre(tag)) {
|
||||||
|| currentTags.contains(QStringView(kTagCode))) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cursor.setPosition(from);
|
cursor.setPosition(from);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue