Update ACE Editor to version 1.2.0

Previously, we were at an ACE editor published between 1.1.8 and 1.1.9. This caused multiple issues and was especially a problem for the upcoming pair programming feature. Further, updating ace is a long-time priority, see https://github.com/openHPI/codeocean/issues/250.

Now, we are not yet updating to the latest version, but rather to the next minor version. This already contains breaking changes, and we are currently interested to keep the number of changes as low as possible. Further updating ACE might be still a future task.

The new ACE version 1.2.0 is taken from this tag: https://github.com/ajaxorg/ace-builds/releases/tag/v1.2.0.
We are using the src build (not minified, not in the noconflict version), since the same was used before as well.

Further, we need to change our migration for storing editor events. Since the table is not yet used (in production), we also update the enum.
This commit is contained in:
Sebastian Serth
2023-09-12 15:09:49 +02:00
parent a7fa9b5b04
commit 735a74901f
325 changed files with 232338 additions and 1057 deletions

View File

@ -64,7 +64,7 @@ $(document).on('turbolinks:load', function () {
},
editor_change(delta, active_file) {
const message = {session_id: session_id, active_file: active_file, delta: delta.data}
const message = {session_id: session_id, active_file: active_file, delta: delta}
this.perform('editor_change', message);
},

View File

@ -356,26 +356,49 @@ var CodeOceanEditor = {
},
handleUTF16Surrogates: function (AceDeltaObject, AceSession) {
if (AceDeltaObject.data === undefined || AceDeltaObject.data.action !== "removeText") {
if (_.isEmpty(AceDeltaObject.lines) || AceDeltaObject.action !== "remove") {
return;
}
const codePoint = AceDeltaObject.data.text.codePointAt(0);
if (0xDC00 <= codePoint && codePoint <= 0xDFFF) {
const firstCodePoint = AceDeltaObject.lines[0].codePointAt(0);
if (0xDC00 <= firstCodePoint && firstCodePoint <= 0xDFFF) {
// The text contains a UTF-16 surrogate pair, and the only the lower part is removed.
// We need to remove the high surrogate pair as well.
const currentCharacter = AceDeltaObject.data.range
const previousCharacter = {
start: {
row: currentCharacter.start.row,
column: currentCharacter.start.column - 1
row: AceDeltaObject.start.row,
column: AceDeltaObject.start.column - 1
},
end: {
row: currentCharacter.start.row,
column: currentCharacter.start.column
row: AceDeltaObject.start.row,
column: AceDeltaObject.start.column
}
}
AceSession.remove(previousCharacter);
const previousCharacterCodePoint = AceSession.getTextRange(previousCharacter).codePointAt(0);
if (0xD800 <= previousCharacterCodePoint && previousCharacterCodePoint <= 0xDBFF) {
AceSession.remove(previousCharacter);
}
}
const lastLine = AceDeltaObject.lines.slice(-1)[0];
const lastCodePoint = lastLine.codePointAt(lastLine.length - 1);
if (0xD800 <= lastCodePoint && lastCodePoint <= 0xDBFF) {
// The text contains a UTF-16 surrogate pair, and the only the higher part is removed.
// We need to remove the high surrogate pair as well.
const nextCharacter = {
start: {
row: AceDeltaObject.end.row,
column: AceDeltaObject.end.column - 1
},
end: {
row: AceDeltaObject.end.row,
column: AceDeltaObject.end.column
}
}
const nextCharacterCodePoint = AceSession.getTextRange(nextCharacter).codePointAt(0);
if (0xDC00 <= nextCharacterCodePoint && nextCharacterCodePoint <= 0xDFFF) {
AceSession.remove(nextCharacter);
}
}
},

View File

@ -21,12 +21,8 @@ class Event::SynchronizedEditor < ApplicationRecord
}, _prefix: true
enum editor_action: {
insertText: 0,
insertLines: 1,
removeText: 2,
removeLines: 3,
changeFold: 4,
removeFold: 5,
insert: 0,
remove: 1,
}, _prefix: true
validates :status, presence: true, if: -> { action_connection_change? }
@ -37,15 +33,14 @@ class Event::SynchronizedEditor < ApplicationRecord
validates :range_start_column, numericality: {only_integer: true, greater_than_or_equal_to: 0}, if: -> { action_editor_change? }
validates :range_end_row, numericality: {only_integer: true, greater_than_or_equal_to: 0}, if: -> { action_editor_change? }
validates :range_end_column, numericality: {only_integer: true, greater_than_or_equal_to: 0}, if: -> { action_editor_change? }
validates :nl, inclusion: {in: %W[\n \r\n]}, if: -> { editor_action_removeLines? }
validate :either_lines_or_text
validates :lines, presence: true, if: -> { action_editor_change? }
def self.create_for_editor_change(event, user, programming_group)
event_copy = event.deep_dup
file = event_copy.delete(:active_file)
delta = event_copy.delete(:delta)
range = delta.delete(:range)
start_range = delta.delete(:start)
end_range = delta.delete(:end)
create!(
user:,
@ -55,12 +50,10 @@ class Event::SynchronizedEditor < ApplicationRecord
editor_action: delta.delete(:action),
file_id: file[:id],
session_id: event_copy.delete(:session_id),
range_start_row: range[:start][:row],
range_start_column: range[:start][:column],
range_end_row: range[:end][:row],
range_end_column: range[:end][:column],
text: delta.delete(:text),
nl: delta.delete(:nl),
range_start_row: start_range[:row],
range_start_column: start_range[:column],
range_end_row: end_range[:row],
range_end_column: end_range[:column],
lines: delta.delete(:lines),
data: data_attribute(event_copy, delta)
)
@ -81,22 +74,4 @@ class Event::SynchronizedEditor < ApplicationRecord
event.presence if event.present? # TODO: As of now, we are storing the `session_id` most of the times. Intended?
end
private_class_method :data_attribute
private
def strip_strings
# trim whitespace from beginning and end of string attributes
# except the `text` and `nl` of Event::SynchronizedEditor
attribute_names.without('text', 'nl').each do |name|
if send(name.to_sym).respond_to?(:strip)
send("#{name}=".to_sym, send(name).strip)
end
end
end
def either_lines_or_text
if [lines, text].count(&:present?) > 1
errors.add(:text, "can't be present if lines is also present")
end
end
end