[Text] Add FixDocumentPartitioner
diff --git a/ecommons/org.eclipse.statet.ecommons.text.core/src/org/eclipse/statet/ecommons/text/core/util/FixDocumentPartitioner.java b/ecommons/org.eclipse.statet.ecommons.text.core/src/org/eclipse/statet/ecommons/text/core/util/FixDocumentPartitioner.java
new file mode 100644
index 0000000..b501f43
--- /dev/null
+++ b/ecommons/org.eclipse.statet.ecommons.text.core/src/org/eclipse/statet/ecommons/text/core/util/FixDocumentPartitioner.java
@@ -0,0 +1,135 @@
+/*=============================================================================#
+ # Copyright (c) 2013, 2021 Stephan Wahlbrink and others.
+ #
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ #
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ #
+ # Contributors:
+ # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ecommons.text.core.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.TypedRegion;
+
+import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+
+@NonNullByDefault
+public class FixDocumentPartitioner implements IDocumentPartitioner {
+
+
+ private final ImList<String> contentTypes;
+
+ private @Nullable IDocument document;
+
+ private final List<ITypedRegion> partitions;
+
+
+ public FixDocumentPartitioner(final ImList<String> contentTypes) {
+ this.contentTypes= contentTypes;
+ this.partitions= new ArrayList<>();
+ }
+
+ public FixDocumentPartitioner(final ImList<String> contentTypes,
+ final ImList<ITypedRegion> partitions) {
+ this.contentTypes= contentTypes;
+ this.partitions= partitions;
+ }
+
+
+ public void append(final String contentType, final int length) {
+ if (this.partitions.isEmpty()) {
+ this.partitions.add(new TypedRegion(0, length, contentType));
+ }
+ else {
+ final ITypedRegion previous= this.partitions.get(this.partitions.size() - 1);
+ if (previous.getType() == contentType) {
+ this.partitions.set(this.partitions.size() - 1, new TypedRegion(
+ previous.getOffset(), previous.getLength() + length, contentType ));
+ }
+ else {
+ this.partitions.add(new TypedRegion(
+ previous.getOffset() + previous.getLength(), length, contentType ));
+ }
+ }
+ }
+
+ @Override
+ public void connect(final IDocument document) {
+ this.document= document;
+ }
+
+ @Override
+ public void disconnect() {
+ this.document= null;
+ }
+
+ @Override
+ public void documentAboutToBeChanged(final DocumentEvent event) {
+ }
+
+ @Override
+ public boolean documentChanged(final DocumentEvent event) {
+ return true;
+ }
+
+ @Override
+ public @NonNull String[] getLegalContentTypes() {
+ return this.contentTypes.toArray(new @NonNull String[this.contentTypes.size()]);
+ }
+
+ private int indexOf(final int offset, final boolean prefereOpen) {
+ final int last= this.partitions.size() - 1;
+ int i= 0;
+ if (prefereOpen) {
+ for (; i < last; i++) {
+ final ITypedRegion partition= this.partitions.get(i);
+ if (offset < partition.getOffset() + partition.getLength()) {
+ return i;
+ }
+ }
+ }
+ // last or prefereOpen
+ for (; i <= last; i++) {
+ final ITypedRegion partition= this.partitions.get(i);
+ if (offset <= partition.getOffset() + partition.getLength()) {
+ return i;
+ }
+ }
+ throw new IndexOutOfBoundsException("offset: " + offset); //$NON-NLS-1$
+ }
+
+ @Override
+ public String getContentType(final int offset) {
+ return this.partitions.get(indexOf(offset, false)).getType();
+ }
+
+ @Override
+ public @NonNull ITypedRegion[] computePartitioning(final int offset, final int length) {
+ final int startIdx= indexOf(offset, false);
+ final int endIdx= indexOf(offset + length, true);
+ final List<ITypedRegion> list= this.partitions.subList(startIdx, endIdx);
+ return list.toArray(new @NonNull ITypedRegion[list.size()]);
+ }
+
+ @Override
+ public ITypedRegion getPartition(final int offset) {
+ return this.partitions.get(indexOf(offset, false));
+ }
+
+}