Skip to content

Commit

Permalink
[INTERNAL][E] Avoid annotation split handling
Browse files Browse the repository at this point in the history
Instead of splitting big annotation if we insert new annotation,
this handling only uses annotations of size 1 and stores multiple
annotations as one entry in the history.
Therefore, the split handling is removed and the issue with
auto-expanding annotations is fixed.
Fix: #32

Furthermore, the history handling for split view is fixed.
The old handling assumed that the entries that are removed
from the history and have to be removed from the models use
the same model as specified in the parameter of insertAnnotation.
The new handling uses the model assigned to the annotation to
remove the annotation.
  • Loading branch information
m273d15 committed Mar 17, 2020
1 parent c5cf634 commit a8c7b2d
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 115 deletions.
3 changes: 0 additions & 3 deletions eclipse/src/saros/editor/EditorManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -1074,9 +1074,6 @@ private void replaceText(SPath path, int offset, String replacedText, String tex
contributionAnnotationManager.insertAnnotation(model, offset, text.length(), source);
}
}

IAnnotationModel model = provider.getAnnotationModel(input);
contributionAnnotationManager.insertAnnotation(model, offset, text.length(), source);
} finally {
provider.disconnect(input);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ protected Pair<IAnnotationModel, List<ContributionAnnotation>> removeHistoryEntr
if (queue.size() == maxHistoryLength) {
List<ContributionAnnotation> annotations = queue.remove();
IAnnotationModel model = annotations.get(0).getModel();
for (ContributionAnnotation a : annotations) {
if (a.getModel() != model) throw new RuntimeException("Different Models");
}
removedEntry = new ImmutablePair<>(model, annotations);
}

Expand Down
121 changes: 80 additions & 41 deletions eclipse/src/saros/editor/internal/ContributionAnnotationManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.log4j.Logger;
import org.eclipse.jface.preference.IPreferenceStore;
Expand Down Expand Up @@ -96,34 +97,99 @@ public ContributionAnnotationManager(
}

/**
* Inserts a contribution annotation to given model if there is not already a contribution
* Inserts contribution annotations to given model if there is not already a contribution
* annotation at given position. This method should be called after the text has changed.
*
* @param model to add the annotation to.
* @param offset start of the annotation to add.
* @param length length of the annotation.
* @param source of the annotation.
*/
// TODO: Rename or split the method. The different cases of this method imply that the concern of
// the method is not just inserting annotations.
public void insertAnnotation(IAnnotationModel model, int offset, int length, User source) {

if (!contribtionAnnotationsEnabled) return;
if (!contribtionAnnotationsEnabled || length < 0) return;

if (length < 0) // why is 0 len allowed ?
return;

/* Return early if there already is an annotation at that offset */
for (Iterator<Annotation> it = model.getAnnotationIterator(); it.hasNext(); ) {
Annotation annotation = it.next();
final ContributionAnnotationHistory history = getHistory(source);
Pair<IAnnotationModel, List<ContributionAnnotation>> annotationsToRemove =
history.removeHistoryEntry();

if (annotation instanceof ContributionAnnotation
&& model.getPosition(annotation).includes(offset)
&& ((ContributionAnnotation) annotation).getSource().equals(source)) {
return;
// Case 1: A delete operation is performed
if (length == 0 && annotationsToRemove != null) {
annotationModelHelper.replaceAnnotationsInModel(
annotationsToRemove.getLeft(), annotationsToRemove.getRight(), Collections.emptyMap());
}
// Cases: An insert operation is performed
else if (length > 0) {
Map<ContributionAnnotation, Position> annotationsToAdd =
createAnnotationsForContributionRange(model, offset, length, source);

history.addNewEntry(new ArrayList<ContributionAnnotation>(annotationsToAdd.keySet()));

// Case 2: An insert operation is performed and the history is full
if (annotationsToRemove != null) {
IAnnotationModel annotationsToRemoveModel = annotationsToRemove.getLeft();

if (annotationsToRemoveModel.equals(model)) {
annotationModelHelper.replaceAnnotationsInModel(
annotationsToRemoveModel, annotationsToRemove.getRight(), annotationsToAdd);
} else {
annotationModelHelper.replaceAnnotationsInModel(
annotationsToRemoveModel, annotationsToRemove.getRight(), Collections.emptyMap());
annotationModelHelper.replaceAnnotationsInModel(
model, Collections.emptyList(), annotationsToAdd);
}
}
// Case 3: An insert operation is performed and the history is not full
else {
annotationModelHelper.replaceAnnotationsInModel(
model, Collections.emptyList(), annotationsToAdd);
}
}
}

/**
* Creates contribution annotations with length 1 for each char contained in the range defined by
* {@code offset} and {@code length}.
*
* @param model the model that is assigned to the created contribution annotations.
* @param offset start of the annotation range.
* @param length length of the annotation range.
* @param source of the annotation.
* @returns a map containing the annotations and their positions
*/
private Map<ContributionAnnotation, Position> createAnnotationsForContributionRange(
IAnnotationModel model, int offset, int length, User source) {

Map<ContributionAnnotation, Position> annotationsToAdd = new HashMap<>();

for (int i = 0; i < length; i++) {
Pair<ContributionAnnotation, Position> positionedAnnotation =
createPositionedAnnotation(model, offset + i, source);
if (positionedAnnotation == null) continue;

annotationsToAdd.put(positionedAnnotation.getKey(), positionedAnnotation.getValue());
}

addContributionAnnotation(
new ContributionAnnotation(source, model), new Position(offset, length));
return annotationsToAdd;
}

/**
* Creates a contribution annotations with length 1 at position {@code offset}.
*
* @param model to add the annotation to.
* @param offset start of the annotation to add.
* @param source of the annotation.
* @returns a pair containing the annotation and its position or <code>null</code> if an
* annotation already exists at the offset value.
*/
private Pair<ContributionAnnotation, Position> createPositionedAnnotation(
IAnnotationModel model, int offset, User source) {
final int ANNOTATION_LENGTH = 1;

return new ImmutablePair<ContributionAnnotation, Position>(
new ContributionAnnotation(source, model), new Position(offset, ANNOTATION_LENGTH));
}

/**
Expand Down Expand Up @@ -183,33 +249,6 @@ private ContributionAnnotationHistory getHistory(final User user) {
user, u -> new ContributionAnnotationHistory(MAX_HISTORY_LENGTH));
}

/**
* Add a contribution annotation to the annotation model and store it into the history of the
* associated user. Old entries are removed from the history and the annotation model.
*/
private void addContributionAnnotation(
final ContributionAnnotation annotation, final Position position) {

final ContributionAnnotationHistory history = getHistory(annotation.getSource());

final Pair<IAnnotationModel, List<ContributionAnnotation>> annotationToRemove =
history.removeHistoryEntry();

history.addNewEntry(annotation);

if (annotationToRemove == null) {
annotation.getModel().addAnnotation(annotation, position);
return;
}

// TODO OPTIMIZE it is usually 2 annotations after the queue is full
annotation.getModel().addAnnotation(annotation, position);

for (ContributionAnnotation a : annotationToRemove.getRight()) {
annotationToRemove.getLeft().removeAnnotation(a);
}
}

/**
* Removes all annotations from all annotation models that are currently stored in the history of
* all users.
Expand Down
Loading

0 comments on commit a8c7b2d

Please sign in to comment.