Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
lixk28 committed Jun 17, 2023
1 parent 9c56828 commit 4f64648
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 66 deletions.
87 changes: 78 additions & 9 deletions Userland/Libraries/LibGUI/TextDocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,11 +341,11 @@ void TextDocument::notify_did_change()
m_regex_needs_update = true;
}

void TextDocument::set_all_cursors(TextPosition const& position)
void TextDocument::set_all_cursors(TextPosition const& position, Vector<TextPosition> const& secondary_positions)
{
if (m_client_notifications_enabled) {
for (auto* client : m_clients)
client->document_did_set_cursor(position);
client->document_did_set_cursor(position, secondary_positions);
}
}

Expand Down Expand Up @@ -859,20 +859,25 @@ TextDocumentUndoCommand::TextDocumentUndoCommand(TextDocument& document)
{
}

InsertTextCommand::InsertTextCommand(TextDocument& document, DeprecatedString const& text, TextPosition const& position)
InsertTextCommand::InsertTextCommand(TextDocument& document, DeprecatedString const& text, TextPosition const& position, Vector<TextPosition> const& secondary_positions)
: TextDocumentUndoCommand(document)
, m_text(text)
, m_range({ position, position })
{
for (TextPosition const& pos : secondary_positions)
m_secondary_ranges.append({ pos, pos });
}

DeprecatedString InsertTextCommand::action_text() const
{
return "Insert Text";
}

// TODO: support multi-cursor merge with other commands
bool InsertTextCommand::merge_with(GUI::Command const& other)
{
// Not insert text command, cannot merge
// insert text command, but commit time expired, cannot merge
if (!is<InsertTextCommand>(other) || commit_time_expired())
return false;

Expand All @@ -885,16 +890,28 @@ bool InsertTextCommand::merge_with(GUI::Command const& other)
if (m_range.start().line() != m_range.end().line())
return false;

if (m_secondary_ranges.size() != typed_other.m_secondary_ranges.size())
return false;
for (size_t i = 0; i < m_secondary_ranges.size(); i++) {
auto const& this_range = m_secondary_ranges[i];
auto const& the_other_range = typed_other.m_secondary_ranges[i];
if (this_range.end() != the_other_range.start() || this_range.start().line() != this_range.end().line())
return false;
}

StringBuilder builder(m_text.length() + typed_other.m_text.length());
builder.append(m_text);
builder.append(typed_other.m_text);
m_text = builder.to_deprecated_string();
m_range.set_end(typed_other.m_range.end());
for (size_t i = 0; i < m_secondary_ranges.size(); i++)
m_secondary_ranges[i].set_end(typed_other.m_secondary_ranges[i].end());

m_timestamp = MonotonicTime::now();
return true;
}

// TODO: support multi-cursor merge with other commands
void InsertTextCommand::perform_formatting(TextDocument::Client const& client)
{
const size_t tab_width = client.soft_tab_width();
Expand Down Expand Up @@ -945,26 +962,44 @@ void InsertTextCommand::perform_formatting(TextDocument::Client const& client)
m_text = builder.to_deprecated_string();
}

// TODO: Add multi-cursor support
void InsertTextCommand::redo()
{
auto new_cursor = m_document.insert_at(m_range.start(), m_text, m_client);
// NOTE: We don't know where the range ends until after doing redo().
// This is okay since we always do redo() after adding this to the undo stack.
m_range.set_end(new_cursor);
m_document.set_all_cursors(new_cursor);

Vector<TextPosition> new_secondary_cursors;
for (TextRange& secondary_range : m_secondary_ranges) {
TextPosition new_secondary_cursor = m_document.insert_at(secondary_range.start(), m_text, m_client);
secondary_range.set_end(new_secondary_cursor);
new_secondary_cursors.append(new_secondary_cursor);
}

m_document.set_all_cursors(new_cursor, new_secondary_cursors);
}

void InsertTextCommand::undo()
{
m_document.remove(m_range);
m_document.set_all_cursors(m_range.start());

Vector<TextPosition> old_secondary_cursors;
for (TextRange const& secondary_range : m_secondary_ranges) {
m_document.remove(secondary_range);
old_secondary_cursors.append(secondary_range.start());
}

m_document.set_all_cursors(m_range.start(), old_secondary_cursors);
}

RemoveTextCommand::RemoveTextCommand(TextDocument& document, DeprecatedString const& text, TextRange const& range, TextPosition const& original_cursor_position)
RemoveTextCommand::RemoveTextCommand(TextDocument& document, DeprecatedString const& text, TextRange const& range, TextPosition const& original_cursor_position, Vector<DeprecatedString> const& secondary_texts, Vector<TextRange> const& secondary_ranges)
: TextDocumentUndoCommand(document)
, m_text(text)
, m_range(range)
, m_original_cursor_position(original_cursor_position)
, m_secondary_texts(secondary_texts)
, m_secondary_ranges(secondary_ranges)
{
}

Expand All @@ -973,6 +1008,7 @@ DeprecatedString RemoveTextCommand::action_text() const
return "Remove Text";
}

// TODO: Add secondary cursors support
bool RemoveTextCommand::merge_with(GUI::Command const& other)
{
if (!is<RemoveTextCommand>(other) || commit_time_expired())
Expand All @@ -985,27 +1021,60 @@ bool RemoveTextCommand::merge_with(GUI::Command const& other)
if (m_range.start().line() != m_range.end().line())
return false;

if (m_secondary_ranges.size() != typed_other.m_secondary_ranges.size())
return false;
for (size_t i = 0; i < m_secondary_ranges.size(); i++) {
auto const& this_range = m_secondary_ranges[i];
auto const& the_other_range = typed_other.m_secondary_ranges[i];
if (this_range.start() != the_other_range.end() || this_range.start().line() != this_range.end().line())
return false;
}

// Merge backspaces
StringBuilder builder(m_text.length() + typed_other.m_text.length());
builder.append(typed_other.m_text);
builder.append(m_text);
m_text = builder.to_deprecated_string();
m_range.set_start(typed_other.m_range.start());

m_timestamp = MonotonicTime::now();
for (size_t i = 0; i < m_secondary_ranges.size(); i++) {
StringBuilder builder(m_secondary_texts[i].length() + typed_other.m_secondary_texts[i].length());
builder.append(typed_other.m_secondary_texts[i]);
builder.append(m_secondary_texts[i]);
m_secondary_texts[i] = builder.to_deprecated_string();
m_secondary_ranges[i].set_start(typed_other.m_secondary_ranges[i].start());
}

m_timestamp = Time::now_monotonic();
return true;
}

// TODO: Add secondary cursors support
void RemoveTextCommand::redo()
{
m_document.remove(m_range);
m_document.set_all_cursors(m_range.start());

Vector<TextPosition> new_secondary_cursors;
for (TextRange const& secondary_range : m_secondary_ranges) {
m_document.remove(secondary_range);
new_secondary_cursors.append(secondary_range.start());
}

m_document.set_all_cursors(m_range.start(), new_secondary_cursors);
}

// TODO: Add secondary cursors support
void RemoveTextCommand::undo()
{
m_document.insert_at(m_range.start(), m_text);
m_document.set_all_cursors(m_original_cursor_position);

Vector<TextPosition> new_secondary_cursors;
for (size_t i = 0; i < m_secondary_ranges.size(); i++) {
TextPosition new_secondary_cursor = m_document.insert_at(m_secondary_ranges[i].start(), m_secondary_texts[i], m_client);
new_secondary_cursors.append(new_secondary_cursor);
}

m_document.set_all_cursors(m_original_cursor_position, new_secondary_cursors);
}

InsertLineCommand::InsertLineCommand(TextDocument& document, TextPosition cursor, DeprecatedString&& text, InsertPosition pos)
Expand Down
11 changes: 7 additions & 4 deletions Userland/Libraries/LibGUI/TextDocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class TextDocument : public RefCounted<TextDocument> {
virtual void document_did_remove_all_lines() = 0;
virtual void document_did_change(AllowCallback = AllowCallback::Yes) = 0;
virtual void document_did_set_text(AllowCallback = AllowCallback::Yes) = 0;
virtual void document_did_set_cursor(TextPosition const&) = 0;
virtual void document_did_set_cursor(TextPosition const&, Vector<TextPosition> const& = {}) = 0;
virtual void document_did_update_undo_stack() = 0;

virtual bool is_automatic_indentation_enabled() const = 0;
Expand Down Expand Up @@ -144,7 +144,7 @@ class TextDocument : public RefCounted<TextDocument> {
UndoStack const& undo_stack() const { return m_undo_stack; }

void notify_did_change();
void set_all_cursors(TextPosition const&);
void set_all_cursors(TextPosition const&, Vector<TextPosition> const& = {});

TextPosition insert_at(TextPosition const&, u32, Client const* = nullptr);
TextPosition insert_at(TextPosition const&, StringView, Client const* = nullptr);
Expand Down Expand Up @@ -237,7 +237,7 @@ class TextDocumentUndoCommand : public Command {

class InsertTextCommand : public TextDocumentUndoCommand {
public:
InsertTextCommand(TextDocument&, DeprecatedString const&, TextPosition const&);
InsertTextCommand(TextDocument&, DeprecatedString const&, TextPosition const&, Vector<TextPosition> const& = {});
virtual ~InsertTextCommand() = default;
virtual void perform_formatting(TextDocument::Client const&) override;
virtual void undo() override;
Expand All @@ -250,11 +250,12 @@ class InsertTextCommand : public TextDocumentUndoCommand {
private:
DeprecatedString m_text;
TextRange m_range;
Vector<TextRange> m_secondary_ranges;
};

class RemoveTextCommand : public TextDocumentUndoCommand {
public:
RemoveTextCommand(TextDocument&, DeprecatedString const&, TextRange const&, TextPosition const&);
RemoveTextCommand(TextDocument&, DeprecatedString const&, TextRange const&, TextPosition const&, Vector<DeprecatedString> const& = {}, Vector<TextRange> const& = {});
virtual ~RemoveTextCommand() = default;
virtual void undo() override;
virtual void redo() override;
Expand All @@ -266,6 +267,8 @@ class RemoveTextCommand : public TextDocumentUndoCommand {
DeprecatedString m_text;
TextRange m_range;
TextPosition m_original_cursor_position;
Vector<DeprecatedString> m_secondary_texts;
Vector<TextRange> m_secondary_ranges;
};

class InsertLineCommand : public TextDocumentUndoCommand {
Expand Down
Loading

0 comments on commit 4f64648

Please sign in to comment.