Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
lixk28 committed Nov 10, 2023
1 parent 6824d2a commit 5dd51f6
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 67 deletions.
2 changes: 1 addition & 1 deletion Userland/DevTools/HackStudio/LanguageServers/FileDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class DefaultDocumentClient final : public GUI::TextDocument::Client {
virtual void document_did_remove_all_lines() override {};
virtual void document_did_change(GUI::AllowCallback) override {};
virtual void document_did_set_text(GUI::AllowCallback) override {};
virtual void document_did_set_cursor(const GUI::TextPosition&) override {};
virtual void document_did_set_cursor(const GUI::TextPosition&, Vector<GUI::TextPosition> const& = {}) override {};
virtual void document_did_update_undo_stack() override { }

virtual bool is_automatic_indentation_enabled() const override { return false; }
Expand Down
80 changes: 71 additions & 9 deletions Userland/Libraries/LibGUI/TextDocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,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 @@ -694,20 +694,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 @@ -720,16 +725,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 @@ -780,26 +797,45 @@ 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, Vector<TextPosition> const& original_secondary_cursor_positions)
: 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)
, m_original_secondary_cursor_positions(original_secondary_cursor_positions)
{
}

Expand All @@ -808,6 +844,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 @@ -820,27 +857,52 @@ 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;
}

void RemoveTextCommand::redo()
{
m_document.remove(m_range);
m_document.set_all_cursors(m_range.start());
// FIXME: a better way?
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);
}

void RemoveTextCommand::undo()
{
m_document.insert_at(m_range.start(), m_text);
m_document.set_all_cursors(m_original_cursor_position);
for (size_t i = 0; i < m_secondary_ranges.size(); i++)
m_document.insert_at(m_secondary_ranges[i].start(), m_secondary_texts[i]);
m_document.set_all_cursors(m_original_cursor_position, m_original_secondary_cursor_positions);
}

InsertLineCommand::InsertLineCommand(TextDocument& document, TextPosition cursor, DeprecatedString&& text, InsertPosition pos)
Expand Down
12 changes: 8 additions & 4 deletions Userland/Libraries/LibGUI/TextDocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class TextDocument : public Syntax::Document {
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 @@ -121,7 +121,7 @@ class TextDocument : public Syntax::Document {
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 @@ -173,7 +173,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 @@ -186,11 +186,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& = {}, Vector<TextPosition> const& = {});
virtual ~RemoveTextCommand() = default;
virtual void undo() override;
virtual void redo() override;
Expand All @@ -202,6 +203,9 @@ 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;
Vector<TextPosition> m_original_secondary_cursor_positions;
};

class InsertLineCommand : public TextDocumentUndoCommand {
Expand Down
Loading

0 comments on commit 5dd51f6

Please sign in to comment.