mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-25 05:55:13 +00:00
LibGUI: Make autoformatting performed on InsertTextCommand visible to on_edit_action
This commit is contained in:
parent
4232874270
commit
8dc0d9b7b7
Notes:
sideshowbarker
2024-07-19 00:45:54 +09:00
Author: https://github.com/ccapitalK Commit: https://github.com/SerenityOS/serenity/commit/8dc0d9b7b7a Pull-request: https://github.com/SerenityOS/serenity/pull/4388 Issue: https://github.com/SerenityOS/serenity/issues/4369
3 changed files with 65 additions and 44 deletions
|
@ -112,6 +112,17 @@ bool TextDocumentLine::ends_in_whitespace() const
|
||||||
return isspace(code_points()[length() - 1]);
|
return isspace(code_points()[length() - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t TextDocumentLine::leading_spaces() const
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
for (; count < m_text.size(); ++count) {
|
||||||
|
if (m_text[count] != ' ') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
String TextDocumentLine::to_utf8() const
|
String TextDocumentLine::to_utf8() const
|
||||||
{
|
{
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
|
@ -661,6 +672,52 @@ InsertTextCommand::InsertTextCommand(TextDocument& document, const String& text,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InsertTextCommand::perform_formatting(const TextDocument::Client& client)
|
||||||
|
{
|
||||||
|
const size_t tab_width = client.soft_tab_width();
|
||||||
|
const auto& dest_line = m_document.line(m_range.start().line());
|
||||||
|
const bool should_auto_indent = client.is_automatic_indentation_enabled();
|
||||||
|
|
||||||
|
StringBuilder builder;
|
||||||
|
size_t column = dest_line.length();
|
||||||
|
size_t line_indentation = dest_line.leading_spaces();
|
||||||
|
bool at_start_of_line = line_indentation == column;
|
||||||
|
|
||||||
|
for (auto input_char : m_text) {
|
||||||
|
if (input_char == '\n') {
|
||||||
|
builder.append('\n');
|
||||||
|
column = 0;
|
||||||
|
if (should_auto_indent) {
|
||||||
|
for (; column < line_indentation; ++column) {
|
||||||
|
builder.append(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
at_start_of_line = true;
|
||||||
|
} else if (input_char == '\t') {
|
||||||
|
size_t next_soft_tab_stop = ((column + tab_width) / tab_width) * tab_width;
|
||||||
|
size_t spaces_to_insert = next_soft_tab_stop - column;
|
||||||
|
for (size_t i = 0; i < spaces_to_insert; ++i) {
|
||||||
|
builder.append(' ');
|
||||||
|
}
|
||||||
|
column = next_soft_tab_stop;
|
||||||
|
if (at_start_of_line) {
|
||||||
|
line_indentation = column;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (input_char == ' ') {
|
||||||
|
if (at_start_of_line) {
|
||||||
|
++line_indentation;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
at_start_of_line = false;
|
||||||
|
}
|
||||||
|
builder.append(input_char);
|
||||||
|
++column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_text = builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
void InsertTextCommand::redo()
|
void InsertTextCommand::redo()
|
||||||
{
|
{
|
||||||
auto new_cursor = m_document.insert_at(m_range.start(), m_text, m_client);
|
auto new_cursor = m_document.insert_at(m_range.start(), m_text, m_client);
|
||||||
|
@ -709,61 +766,21 @@ TextPosition TextDocument::insert_at(const TextPosition& position, const StringV
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextPosition TextDocument::insert_at(const TextPosition& position, u32 code_point, const Client* client)
|
TextPosition TextDocument::insert_at(const TextPosition& position, u32 code_point, const Client*)
|
||||||
{
|
{
|
||||||
bool automatic_indentation_enabled = client ? client->is_automatic_indentation_enabled() : false;
|
|
||||||
size_t m_soft_tab_width = client ? client->soft_tab_width() : 4;
|
|
||||||
|
|
||||||
bool at_head = position.column() == 0;
|
|
||||||
bool at_tail = position.column() == line(position.line()).length();
|
|
||||||
if (code_point == '\n') {
|
if (code_point == '\n') {
|
||||||
if (at_tail || at_head) {
|
|
||||||
String new_line_contents;
|
|
||||||
if (automatic_indentation_enabled && at_tail) {
|
|
||||||
size_t leading_spaces = 0;
|
|
||||||
auto& old_line = lines()[position.line()];
|
|
||||||
for (size_t i = 0; i < old_line.length(); ++i) {
|
|
||||||
if (old_line.code_points()[i] == ' ')
|
|
||||||
++leading_spaces;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (leading_spaces)
|
|
||||||
new_line_contents = String::repeated(' ', leading_spaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t row = position.line();
|
|
||||||
Vector<u32> line_content;
|
|
||||||
for (size_t i = position.column(); i < line(row).length(); i++)
|
|
||||||
line_content.append(line(row).code_points()[i]);
|
|
||||||
insert_line(position.line() + (at_tail ? 1 : 0), make<TextDocumentLine>(*this, new_line_contents));
|
|
||||||
notify_did_change();
|
|
||||||
return { position.line() + 1, line(position.line() + 1).length() };
|
|
||||||
}
|
|
||||||
auto new_line = make<TextDocumentLine>(*this);
|
auto new_line = make<TextDocumentLine>(*this);
|
||||||
new_line->append(*this, line(position.line()).code_points() + position.column(), line(position.line()).length() - position.column());
|
new_line->append(*this, line(position.line()).code_points() + position.column(), line(position.line()).length() - position.column());
|
||||||
|
|
||||||
Vector<u32> line_content;
|
|
||||||
for (size_t i = 0; i < new_line->length(); i++)
|
|
||||||
line_content.append(new_line->code_points()[i]);
|
|
||||||
line(position.line()).truncate(*this, position.column());
|
line(position.line()).truncate(*this, position.column());
|
||||||
insert_line(position.line() + 1, move(new_line));
|
insert_line(position.line() + 1, move(new_line));
|
||||||
notify_did_change();
|
notify_did_change();
|
||||||
return { position.line() + 1, 0 };
|
return { position.line() + 1, 0 };
|
||||||
}
|
} else {
|
||||||
if (code_point == '\t') {
|
|
||||||
size_t next_soft_tab_stop = ((position.column() + m_soft_tab_width) / m_soft_tab_width) * m_soft_tab_width;
|
|
||||||
size_t spaces_to_insert = next_soft_tab_stop - position.column();
|
|
||||||
for (size_t i = 0; i < spaces_to_insert; ++i) {
|
|
||||||
line(position.line()).insert(*this, position.column(), ' ');
|
|
||||||
}
|
|
||||||
notify_did_change();
|
|
||||||
return { position.line(), next_soft_tab_stop };
|
|
||||||
}
|
|
||||||
line(position.line()).insert(*this, position.column(), code_point);
|
line(position.line()).insert(*this, position.column(), code_point);
|
||||||
notify_did_change();
|
notify_did_change();
|
||||||
return { position.line(), position.column() + 1 };
|
return { position.line(), position.column() + 1 };
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TextDocument::remove(const TextRange& unnormalized_range)
|
void TextDocument::remove(const TextRange& unnormalized_range)
|
||||||
{
|
{
|
||||||
|
|
|
@ -194,6 +194,7 @@ public:
|
||||||
Optional<size_t> last_non_whitespace_column() const;
|
Optional<size_t> last_non_whitespace_column() const;
|
||||||
bool ends_in_whitespace() const;
|
bool ends_in_whitespace() const;
|
||||||
bool is_empty() const { return length() == 0; }
|
bool is_empty() const { return length() == 0; }
|
||||||
|
size_t leading_spaces() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// NOTE: This vector is null terminated.
|
// NOTE: This vector is null terminated.
|
||||||
|
@ -204,6 +205,7 @@ class TextDocumentUndoCommand : public Command {
|
||||||
public:
|
public:
|
||||||
TextDocumentUndoCommand(TextDocument&);
|
TextDocumentUndoCommand(TextDocument&);
|
||||||
virtual ~TextDocumentUndoCommand();
|
virtual ~TextDocumentUndoCommand();
|
||||||
|
virtual void perform_formatting(const TextDocument::Client&) { }
|
||||||
|
|
||||||
void execute_from(const TextDocument::Client& client)
|
void execute_from(const TextDocument::Client& client)
|
||||||
{
|
{
|
||||||
|
@ -220,6 +222,7 @@ protected:
|
||||||
class InsertTextCommand : public TextDocumentUndoCommand {
|
class InsertTextCommand : public TextDocumentUndoCommand {
|
||||||
public:
|
public:
|
||||||
InsertTextCommand(TextDocument&, const String&, const TextPosition&);
|
InsertTextCommand(TextDocument&, const String&, const TextPosition&);
|
||||||
|
virtual void perform_formatting(const TextDocument::Client&) override;
|
||||||
virtual void undo() override;
|
virtual void undo() override;
|
||||||
virtual void redo() override;
|
virtual void redo() override;
|
||||||
virtual bool is_insert_text() const override { return true; }
|
virtual bool is_insert_text() const override { return true; }
|
||||||
|
|
|
@ -260,6 +260,7 @@ private:
|
||||||
inline void execute(Args&&... args)
|
inline void execute(Args&&... args)
|
||||||
{
|
{
|
||||||
auto command = make<T>(*m_document, forward<Args>(args)...);
|
auto command = make<T>(*m_document, forward<Args>(args)...);
|
||||||
|
command->perform_formatting(*this);
|
||||||
on_edit_action(*command);
|
on_edit_action(*command);
|
||||||
command->execute_from(*this);
|
command->execute_from(*this);
|
||||||
m_document->add_to_undo_stack(move(command));
|
m_document->add_to_undo_stack(move(command));
|
||||||
|
|
Loading…
Add table
Reference in a new issue