mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 17:57:34 +00:00
LibMarkdown: Add blockquote support to LineIterator
This patch adds contexts to line iterator for nesting list items and blockquotes. It also incidentally makes the api for LineIterator simpler, and will make it easier to add other containers in the future.
This commit is contained in:
parent
4b091a7cc2
commit
2227a80f34
3 changed files with 79 additions and 32 deletions
|
@ -4,38 +4,62 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/Format.h>
|
||||||
#include <LibMarkdown/LineIterator.h>
|
#include <LibMarkdown/LineIterator.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
|
||||||
bool LineIterator::is_indented(StringView const& line) const
|
void LineIterator::reset_ignore_prefix()
|
||||||
{
|
{
|
||||||
if (line.is_whitespace())
|
for (auto& context : m_context_stack) {
|
||||||
return true;
|
context.ignore_prefix = false;
|
||||||
|
|
||||||
if (line.length() < m_indent)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_indent; ++i) {
|
|
||||||
if (line[i] != ' ')
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
Optional<StringView> LineIterator::match_context(StringView const& line) const
|
||||||
|
{
|
||||||
|
bool is_ws = line.is_whitespace();
|
||||||
|
size_t offset = 0;
|
||||||
|
for (auto& context : m_context_stack) {
|
||||||
|
switch (context.type) {
|
||||||
|
case Context::Type::ListItem:
|
||||||
|
if (is_ws)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (offset + context.indent > line.length())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (!context.ignore_prefix && !line.substring_view(offset, context.indent).is_whitespace())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
offset += context.indent;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case Context::Type::BlockQuote:
|
||||||
|
for (; offset < line.length() && line[offset] == ' '; ++offset) { }
|
||||||
|
if (offset >= line.length() || line[offset] != '>') {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
++offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset > line.length())
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return line.substring_view(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LineIterator::is_end() const
|
bool LineIterator::is_end() const
|
||||||
{
|
{
|
||||||
return m_iterator.is_end() || (!m_ignore_prefix_mode && !is_indented(*m_iterator));
|
return m_iterator.is_end() || !match_context(*m_iterator).has_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
StringView LineIterator::operator*() const
|
StringView LineIterator::operator*() const
|
||||||
{
|
{
|
||||||
VERIFY(m_ignore_prefix_mode || is_indented(*m_iterator));
|
auto line = match_context(*m_iterator);
|
||||||
if (m_iterator->is_whitespace())
|
VERIFY(line.has_value());
|
||||||
return *m_iterator;
|
return line.value();
|
||||||
|
|
||||||
return m_iterator->substring_view(m_indent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,22 @@ private:
|
||||||
|
|
||||||
class LineIterator {
|
class LineIterator {
|
||||||
public:
|
public:
|
||||||
LineIterator(Vector<StringView>::ConstIterator const& lines, size_t indent = 0)
|
struct Context {
|
||||||
|
enum class Type {
|
||||||
|
ListItem,
|
||||||
|
BlockQuote,
|
||||||
|
};
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
size_t indent;
|
||||||
|
bool ignore_prefix;
|
||||||
|
|
||||||
|
static Context list_item(size_t indent) { return { Type::ListItem, indent, true }; }
|
||||||
|
static Context block_quote() { return { Type::BlockQuote, 0, false }; }
|
||||||
|
};
|
||||||
|
|
||||||
|
LineIterator(Vector<StringView>::ConstIterator const& lines)
|
||||||
: m_iterator(lines)
|
: m_iterator(lines)
|
||||||
, m_indent(indent)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,23 +66,35 @@ public:
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
LineIterator operator+(ptrdiff_t delta) const { return LineIterator { m_iterator + delta, m_indent }; }
|
LineIterator operator+(ptrdiff_t delta) const
|
||||||
LineIterator operator-(ptrdiff_t delta) const { return LineIterator { m_iterator - delta, m_indent }; }
|
{
|
||||||
|
LineIterator copy = *this;
|
||||||
|
copy.reset_ignore_prefix();
|
||||||
|
copy.m_iterator = copy.m_iterator + delta;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
LineIterator operator-(ptrdiff_t delta) const
|
||||||
|
{
|
||||||
|
LineIterator copy = *this;
|
||||||
|
copy.reset_ignore_prefix();
|
||||||
|
copy.m_iterator = copy.m_iterator - delta;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
ptrdiff_t operator-(LineIterator other) const { return m_iterator - other.m_iterator; }
|
ptrdiff_t operator-(LineIterator other) const { return m_iterator - other.m_iterator; }
|
||||||
|
|
||||||
FakePtr<StringView> operator->() const { return FakePtr<StringView>(operator*()); }
|
FakePtr<StringView> operator->() const { return FakePtr<StringView>(operator*()); }
|
||||||
|
|
||||||
size_t indent() const { return m_indent; }
|
void push_context(Context context) { m_context_stack.append(move(context)); }
|
||||||
void set_indent(size_t indent) { m_indent = indent; }
|
void pop_context() { m_context_stack.take_last(); }
|
||||||
void ignore_next_prefix() { m_ignore_prefix_mode = true; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void reset_ignore_prefix() { m_ignore_prefix_mode = false; }
|
void reset_ignore_prefix();
|
||||||
bool is_indented(StringView const& line) const;
|
Optional<StringView> match_context(StringView const& line) const;
|
||||||
|
|
||||||
Vector<StringView>::ConstIterator m_iterator;
|
Vector<StringView>::ConstIterator m_iterator;
|
||||||
size_t m_indent;
|
Vector<Context> m_context_stack;
|
||||||
bool m_ignore_prefix_mode { false };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,16 +121,14 @@ OwnPtr<List> List::parse(LineIterator& lines)
|
||||||
|
|
||||||
is_tight = is_tight && !has_trailing_blank_lines;
|
is_tight = is_tight && !has_trailing_blank_lines;
|
||||||
|
|
||||||
size_t saved_indent = lines.indent();
|
lines.push_context(LineIterator::Context::list_item(offset));
|
||||||
lines.set_indent(saved_indent + offset);
|
|
||||||
lines.ignore_next_prefix();
|
|
||||||
|
|
||||||
auto list_item = ContainerBlock::parse(lines);
|
auto list_item = ContainerBlock::parse(lines);
|
||||||
is_tight = is_tight && !list_item->has_blank_lines();
|
is_tight = is_tight && !list_item->has_blank_lines();
|
||||||
has_trailing_blank_lines = has_trailing_blank_lines || list_item->has_trailing_blank_lines();
|
has_trailing_blank_lines = has_trailing_blank_lines || list_item->has_trailing_blank_lines();
|
||||||
items.append(move(list_item));
|
items.append(move(list_item));
|
||||||
|
|
||||||
lines.set_indent(saved_indent);
|
lines.pop_context();
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue