diff options
author | Paul Pazderski | 2019-03-27 12:15:08 +0000 |
---|---|---|
committer | Paul Pazderski | 2019-11-16 20:38:07 +0000 |
commit | 0fcbf89cc35b852138f2fe7750773bb9eb59457e (patch) | |
tree | 15554f9c580110a791b1ce98ab50578c50d4038b | |
parent | 2e6899ce766c03ac6e36f6579f75d0867abfc49b (diff) | |
download | eclipse.platform.debug-0fcbf89cc35b852138f2fe7750773bb9eb59457e.tar.gz eclipse.platform.debug-0fcbf89cc35b852138f2fe7750773bb9eb59457e.tar.xz eclipse.platform.debug-0fcbf89cc35b852138f2fe7750773bb9eb59457e.zip |
Bug 551745 - [console] Revise IOConsolePartitioner output appending andI20191118-0600
initialization
Remove, rename or rework some fields and simplify how stream output is
appended to document. (without losing existing functionality)
Second try. This time with deadlock protection.
Change-Id: I8d80ade8bb521895c97d6e08d2f520dd9b182a3b
Signed-off-by: Paul Pazderski <paul-eclipse@ppazderski.de>
3 files changed, 313 insertions, 242 deletions
diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java b/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java index dd2000786..77e6172fb 100644 --- a/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java +++ b/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2018 IBM Corporation and others. + * Copyright (c) 2000, 2019 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.List; import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.text.IDocument; import org.eclipse.ui.WorkbenchEncoding; import org.eclipse.ui.internal.console.IOConsolePage; import org.eclipse.ui.internal.console.IOConsolePartitioner; @@ -42,7 +43,7 @@ public class IOConsole extends TextConsole { /** * The document partitioner */ - private IOConsolePartitioner partitioner; + private final IOConsolePartitioner partitioner; /** * The stream from which user input may be read @@ -52,12 +53,12 @@ public class IOConsole extends TextConsole { /** * A collection of open streams connected to this console. */ - private List<Closeable> openStreams = Collections.synchronizedList(new ArrayList<>()); + private final List<Closeable> openStreams = Collections.synchronizedList(new ArrayList<>()); /** * The encoding used to for displaying console output. */ - private Charset charset; + private final Charset charset; /** @@ -115,10 +116,11 @@ public class IOConsole extends TextConsole { inputStream = new IOConsoleInputStream(this); openStreams.add(inputStream); } - - if (inputStream instanceof IOConsoleInputStream) { - partitioner = new IOConsolePartitioner((IOConsoleInputStream) inputStream, this); - partitioner.connect(getDocument()); + partitioner = new IOConsolePartitioner(this); + final IDocument document = getDocument(); + if (document != null) { + partitioner.connect(document); + document.setDocumentPartitioner(partitioner); } } @@ -174,6 +176,12 @@ public class IOConsole extends TextConsole { /** * Returns the input stream connected to the keyboard. + * <p> + * Note: It returns the stream connected to keyboard. There is no guarantee to + * get the stream last set with {@link #setInputStream(InputStream)}. The return + * value might be <code>null</code> if the current input stream is not connected + * to the keyboard. + * </p> * * @return the input stream connected to the keyboard. */ @@ -283,9 +291,7 @@ public class IOConsole extends TextConsole { @Override public void clearConsole() { - if (partitioner != null) { - partitioner.clearBuffer(); - } + partitioner.clearBuffer(); } /** @@ -293,28 +299,38 @@ public class IOConsole extends TextConsole { */ @Override protected void dispose() { - super.dispose(); - partitioner.disconnect(); - //make a copy of the open streams and close them all - //a copy is needed as close the streams results in a callback that - //removes the streams from the openStreams collection (bug 152794) - List<Closeable> list = new ArrayList<>(openStreams); - for (Closeable closable : list) { - try { - closable.close(); - } catch (IOException e) { - // e.printStackTrace(); + // Get lock on ourself before closing. Closing streams check for console finish. + // Finish check need lock on (this) console. Since closing also lock on stream + // this can deadlock with other threads (double) closing streams at same time + // but already own a lock on console. (bug 551902) + // Long story short. Before closing IOConsole...Stream get lock from associated + // console to prevent deadlocks. + synchronized (this) { + // make a copy of the open streams and close them all + // a copy is needed as close the streams results in a callback that + // removes the streams from the openStreams collection (bug 152794) + List<Closeable> list = new ArrayList<>(openStreams); + for (Closeable closable : list) { + try { + closable.close(); + } catch (IOException e) { + // e.printStackTrace(); + } } + inputStream = null; } - inputStream = null; + + final IDocument document = partitioner.getDocument(); + document.setDocumentPartitioner(null); + partitioner.disconnect(); + + super.dispose(); } /** - * Returns the encoding for this console, or <code>null</code> to indicate - * default encoding. + * Returns the encoding for this console. * - * @return the encoding set for this console, or <code>null</code> to indicate - * default encoding + * @return the encoding set for this console * @since 3.3 */ public String getEncoding() { @@ -322,11 +338,9 @@ public class IOConsole extends TextConsole { } /** - * Returns the Charset for this console, or <code>null</code> to indicate - * default encoding. + * Returns the Charset for this console. * - * @return the Charset for this console, or <code>null</code> to indicate - * default encoding + * @return the Charset for this console * @since 3.7 */ public Charset getCharset() { diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartition.java b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartition.java index 3f43137d0..34ffa88c3 100644 --- a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartition.java +++ b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartition.java @@ -160,7 +160,7 @@ public class IOConsolePartition implements ITypedRegion { */ private int getFontStyle() { if (type.equals(INPUT_PARTITION_TYPE)) { - return inputStream.getFontStyle(); + return inputStream != null ? inputStream.getFontStyle() : 0; } return outputStream.getFontStyle(); } @@ -174,7 +174,7 @@ public class IOConsolePartition implements ITypedRegion { */ public Color getColor() { if (type.equals(INPUT_PARTITION_TYPE)) { - return inputStream.getColor(); + return inputStream != null ? inputStream.getColor() : null; } return outputStream.getColor(); } diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartitioner.java b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartitioner.java index 79dd58115..477e7a16b 100644 --- a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartitioner.java +++ b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartitioner.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; +import java.util.Objects; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IProgressMonitor; @@ -59,6 +60,25 @@ import org.eclipse.ui.progress.WorkbenchJob; public class IOConsolePartitioner implements IConsoleDocumentPartitioner, IConsoleDocumentPartitionerExtension, IDocumentPartitionerExtension { /** + * Enumeration used to distinct sources of document updates. (especially to + * distinct updates triggered by this partitioner from other document changes) + */ + private enum DocUpdateType { + /** + * Default if reason for document change is not known. Document change is + * interpreted as user input. + */ + INPUT, + /** + * Document update was triggered from this partitioner by appending content + * received from output streams. + */ + OUTPUT, + /** Document update was triggered from this partitioner's {@link TrimJob}. */ + TRIM, + } + + /** * If true validate partitioning after changes and do other additional * assertions. Useful for developing/debugging. */ @@ -69,7 +89,6 @@ public class IOConsolePartitioner */ private static final Comparator<IRegion> CMP_REGION_BY_OFFSET = Comparator.comparing(IRegion::getOffset); - private PendingPartition consoleClosedPartition; /** The connected {@link IDocument} this partitioner manages. */ private IDocument document; /** @@ -77,59 +96,60 @@ public class IOConsolePartitioner * {@link IRegion#getOffset()} and not contain <code>null</code> or 0-length * elements. (see also {@link #checkPartitions()}) */ - private ArrayList<IOConsolePartition> partitions; + private final ArrayList<IOConsolePartition> partitions = new ArrayList<>(); /** Blocks of data that have not yet been appended to the document. */ - private ArrayList<PendingPartition> pendingPartitions; - /** - * A list of PendingPartitions to be appended by the updateJob - */ - private ArrayList<PendingPartition> updatePartitions; - /** - * Job that appends pending partitions to the document. - */ - private QueueProcessingJob queueJob; + private final ArrayList<PendingPartition> pendingPartitions = new ArrayList<>(); + /** Total length of pending partitions content. */ + private int pendingSize; + /** Job that appends pending partitions to the document. */ + private final QueueProcessingJob queueJob = new QueueProcessingJob(); /** Job that trims console content if it exceeds {@link #highWaterMark}. */ - private TrimJob trimJob = new TrimJob(); - /** The input stream attached to this document. */ - private IOConsoleInputStream inputStream; + private final TrimJob trimJob = new TrimJob(); /** - * Flag to indicate that the updateJob is updating the document. + * Reason for document update. Set before changing document inside this + * partitioner to prevent that change is interpreted as user input. + * <p> + * Automatically reset to {@link DocUpdateType#INPUT} after every document + * change. + * </p> */ - private boolean updateInProgress; + private DocUpdateType updateType = DocUpdateType.INPUT; + private IRegion changedRegion; /** * A list of partitions containing input from the console, that have not been * appended to the input stream yet. No guarantees on element order. */ private ArrayList<IOConsolePartition> inputPartitions; /** - * offset used by updateJob - */ - private int firstOffset; - /** * A matcher to search for legal line delimiters in new input. Never * <code>null</code> but match nothing if no document connected. */ private MultiStringMatcher legalLineDelimiterMatcher; + /** + * The high mark for console content trimming. If console content exceeds this + * length trimming is scheduled. Trimming is disabled if value is negative. + */ private int highWaterMark = -1; private int lowWaterMark = -1; - private boolean connected = false; /** The partitioned {@link IOConsole}. */ private IOConsole console; + /** Set after console signaled that all streams are closed. */ + private volatile boolean streamsClosed; + /** - * Lock for appending to and removing from the document - used - * to synchronize addition of new text/partitions in the update - * job and handling buffer overflow/clearing of the console. + * Create new partitioner for an {@link IOConsole}. + * <p> + * The partitioner must be explicit {@link #connect(IDocument) connected} with + * the consoles {@link IDocument}. + * </p> + * + * @param console the partitioned console. Not <code>null</code>. */ - private Object overflowLock = new Object(); - - - private int fBuffer; - - public IOConsolePartitioner(IOConsoleInputStream inputStream, IOConsole console) { - this.inputStream = inputStream; - this.console = console; + public IOConsolePartitioner(IOConsole console) { + this.console = Objects.requireNonNull(console); + queueJob.setRule(console.getSchedulingRule()); trimJob.setRule(console.getSchedulingRule()); } @@ -144,27 +164,63 @@ public class IOConsolePartitioner @Override public void connect(IDocument doc) { - document = doc; - document.setDocumentPartitioner(this); - legalLineDelimiterMatcher = MultiStringMatcher.create(document.getLegalLineDelimiters()); - partitions = new ArrayList<>(); - pendingPartitions = new ArrayList<>(); - inputPartitions = new ArrayList<>(); - queueJob = new QueueProcessingJob(); - queueJob.setSystem(true); - queueJob.setPriority(Job.INTERACTIVE); - queueJob.setRule(console.getSchedulingRule()); - connected = true; + if (doc == document) { + return; + } + disconnect(); + if (doc != null) { + synchronized (partitions) { + inputPartitions = new ArrayList<>(); + document = doc; + legalLineDelimiterMatcher = MultiStringMatcher.create(document.getLegalLineDelimiters()); + } + } + } + + @Override + public void disconnect() { + synchronized (pendingPartitions) { + pendingPartitions.clear(); + pendingSize = 0; + pendingPartitions.notifyAll(); + } + synchronized (partitions) { + trimJob.cancel(); + queueJob.cancel(); + legalLineDelimiterMatcher = null; + document = null; + inputPartitions = null; + partitions.clear(); + } } + /** + * Get high water mark. + * + * @return the trim if exceeded mark + * @see IOConsole#getHighWaterMark() + */ public int getHighWaterMark() { return highWaterMark; } + /** + * Get low water mark. + * + * @return the trim to this length mark + * @see IOConsole#getLowWaterMark() + */ public int getLowWaterMark() { return lowWaterMark; } + /** + * Set low and high water marks. + * + * @param low the trim to this length mark + * @param high the trim if exceeded mark + * @see IOConsole#setWaterMarks(int, int) + */ public void setWaterMarks(int low, int high) { lowWaterMark = low; highWaterMark = high; @@ -175,22 +231,30 @@ public class IOConsolePartitioner * Notification from the console that all of its streams have been closed. */ public void streamsClosed() { - consoleClosedPartition = new PendingPartition(null, null); - synchronized (pendingPartitions) { - pendingPartitions.add(consoleClosedPartition); + if (streamsClosed) { + log(IStatus.ERROR, "Streams are already closed."); //$NON-NLS-1$ + return; } - queueJob.schedule(); //ensure that all pending partitions are processed. + streamsClosed = true; + checkFinished(); } - @Override - public void disconnect() { - synchronized (overflowLock) { - document = null; - partitions.clear(); - connected = false; - try { - inputStream.close(); - } catch (IOException e) { + /** + * Check if partitioner is finished and does not expect any new data appended to + * document. + */ + private void checkFinished() { + if (streamsClosed) { + // do not expect new data since all streams are closed + // check if pending data is queued + final boolean morePending; + synchronized (pendingPartitions) { + morePending = !pendingPartitions.isEmpty(); + } + if (morePending) { + queueJob.schedule(); + } else { + console.partitionerFinished(); } } } @@ -320,15 +384,17 @@ public class IOConsolePartitioner /** * Enforces the buffer size. + * <p> * When the number of lines in the document exceeds the high water mark, the - * beginning of the document is trimmed until the number of lines equals the - * low water mark. + * beginning of the document is trimmed until the number of lines equals the low + * water mark. + * </p> */ private void checkBufferSize() { if (document != null && highWaterMark > 0) { int length = document.getLength(); if (length > highWaterMark) { - if (trimJob.getState() == Job.NONE) { //if the job isn't already running + if (trimJob.getState() == Job.NONE) { // if the job isn't already running trimJob.setOffset(length - lowWaterMark); trimJob.schedule(); } @@ -337,57 +403,50 @@ public class IOConsolePartitioner } /** - * Clears the console + * Clears the console content. */ public void clearBuffer() { - synchronized (overflowLock) { - trimJob.setOffset(-1); - trimJob.schedule(); - } + trimJob.setOffset(-1); + trimJob.schedule(); } @Override public IRegion documentChanged2(DocumentEvent event) { - if (document == null) { - return null; //another thread disconnected the partitioner - } - if (document.getLength() == 0) { // document cleared - synchronized (partitions) { - partitions.clear(); - inputPartitions.clear(); - } - return new Region(0, 0); - } - - if (updateInProgress) { - synchronized(partitions) { - if (updatePartitions != null) { - IOConsolePartition lastPartition = getPartitionByIndex(partitions.size() - 1); - for (PendingPartition pp : updatePartitions) { - if (pp == consoleClosedPartition) { - continue; - } - - int ppLen = pp.text.length(); - if (lastPartition != null && lastPartition.getOutputStream() == pp.stream) { - int len = lastPartition.getLength(); - lastPartition.setLength(len + ppLen); - } else { - IOConsolePartition partition = new IOConsolePartition(firstOffset, pp.stream); - partition.setLength(ppLen); - lastPartition = partition; - partitions.add(partition); - } - firstOffset += ppLen; - } + try { + if (document != event.getDocument()) { + log(IStatus.WARNING, "IOConsolePartitioner is connected to wrong document."); //$NON-NLS-1$ + return null; + } + if (document.getLength() == 0) { // document cleared + synchronized (partitions) { + partitions.clear(); + inputPartitions.clear(); } + return new Region(0, 0); } - } else { + synchronized (partitions) { - return applyUserInput(event); + switch (updateType) { + case INPUT: + return applyUserInput(event); + + // update and trim jobs are triggered by this partitioner and all partitioning + // changes are applied separately + case OUTPUT: + return changedRegion; + case TRIM: + return null; // trim does not change partition types + + default: + log(IStatus.ERROR, "Invalid enum value " + updateType); //$NON-NLS-1$ + return null; + } } + } finally { + // always reset type since all change events not triggered by this partitioner + // are interpreted as user input + updateType = DocUpdateType.INPUT; } - return new Region(event.fOffset, event.fText.length()); } /** @@ -404,10 +463,14 @@ public class IOConsolePartitioner * @return the region of the document in which the partition type changed or * <code>null</code> */ + // Required for a false 'resource not closed' warning on inputStream. + // This input stream must not be closed by this method. + @SuppressWarnings("resource") private IRegion applyUserInput(DocumentEvent event) { final int eventTextLength = event.getText() != null ? event.getText().length() : 0; final int offset = event.getOffset(); final int amountDeleted = event.getLength(); + final IOConsoleInputStream inputStream = console.getInputStream(); // do not close this stream if (amountDeleted == 0 && eventTextLength == 0) { // event did not changed document @@ -484,7 +547,9 @@ public class IOConsolePartitioner if (ASSERT) { Assert.isTrue(inputLine.length() > 0); } - inputStream.appendData(inputLine.toString()); + if (inputStream != null) { + inputStream.appendData(inputLine.toString()); + } } Assert.isTrue(newTextOffset > textOffset); // can prevent infinity loop textOffset = newTextOffset; @@ -558,10 +623,6 @@ public class IOConsolePartitioner return newPartition; } - private void setUpdateInProgress(boolean b) { - updateInProgress = b; - } - /** * A stream has been appended, add to pendingPartions list and schedule * updateJob. updateJob is scheduled with a slight delay, this allows the @@ -576,31 +637,34 @@ public class IOConsolePartitioner if (document == null) { throw new IOException("Document is closed"); //$NON-NLS-1$ } - synchronized(pendingPartitions) { - PendingPartition last = pendingPartitions.size() > 0 ? pendingPartitions.get(pendingPartitions.size()-1) : null; - if (last != null && last.stream == stream) { - last.append(s); + if (s == null) { + return; + } + synchronized (pendingPartitions) { + final PendingPartition lastPending = pendingPartitions.size() > 0 + ? pendingPartitions.get(pendingPartitions.size() - 1) + : null; + if (lastPending != null && lastPending.stream == stream) { + lastPending.append(s); } else { pendingPartitions.add(new PendingPartition(stream, s)); - if (fBuffer > 1000) { - queueJob.schedule(); - } else { - queueJob.schedule(50); - } } - if (fBuffer > 160000) { - if(Display.getCurrent() == null){ + if (pendingSize > 1000) { + queueJob.schedule(); + } else { + queueJob.schedule(50); + } + + if (pendingSize > 160000) { + if (Display.getCurrent() == null) { try { pendingPartitions.wait(); } catch (InterruptedException e) { } } else { - /* - * if we are in UI thread we cannot lock it, so process - * queued output. - */ - processQueue(); + // if we are in UI thread we cannot lock it, so process queued output. + processPendingPartitions(); } } } @@ -615,30 +679,30 @@ public class IOConsolePartitioner PendingPartition(IOConsoleOutputStream stream, String text) { this.stream = stream; - if (text != null) { - append(text); - } + append(text); } void append(String moreText) { text.append(moreText); - fBuffer += moreText.length(); + pendingSize += moreText.length(); } } /** - * Updates the document. Will append everything that is available before - * finishing. + * Updates the document and partitioning structure. Will append everything + * received from output streams that is available before finishing. */ private class QueueProcessingJob extends UIJob { QueueProcessingJob() { super("IOConsole Updater"); //$NON-NLS-1$ + setSystem(true); + setPriority(Job.INTERACTIVE); } @Override public IStatus runInUIThread(IProgressMonitor monitor) { - processQueue(); + processPendingPartitions(); if (ASSERT) { checkPartitions(); } @@ -646,74 +710,67 @@ public class IOConsolePartitioner } /* - * Job will process as much as it can each time it's run, but it gets - * scheduled everytime a PendingPartition is added to the list, meaning - * that this job could get scheduled unnecessarily in cases of heavy output. - * Note however, that schedule() will only reschedule a running/scheduled Job - * once even if it's called many times. + * Job will process as much as it can each time it's run, but it gets scheduled + * everytime a PendingPartition is added to the list, meaning that this job + * could get scheduled unnecessarily in cases of heavy output. Note however, + * that schedule() will only reschedule a running/scheduled Job once even if + * it's called many times. */ @Override public boolean shouldRun() { - boolean shouldRun = connected && pendingPartitions != null && pendingPartitions.size() > 0; - return shouldRun; - } - } - - void processQueue() { - synchronized (overflowLock) { - ArrayList<PendingPartition> pendingCopy = new ArrayList<>(); - StringBuilder buffer = null; - boolean consoleClosed = false; - synchronized(pendingPartitions) { - pendingCopy.addAll(pendingPartitions); - pendingPartitions.clear(); - fBuffer = 0; - pendingPartitions.notifyAll(); - } - // determine buffer size - int size = 0; - for (PendingPartition pp : pendingCopy) { - if (pp != consoleClosedPartition) { - size+= pp.text.length(); - } - } - buffer = new StringBuilder(size); - for (PendingPartition pp : pendingCopy) { - if (pp != consoleClosedPartition) { - buffer.append(pp.text); - } else { - consoleClosed = true; - } + synchronized (pendingPartitions) { + final boolean shouldRun = pendingPartitions.size() > 0; + return shouldRun; } - if (connected) { - setUpdateInProgress(true); - updatePartitions = pendingCopy; - firstOffset = document.getLength(); - try { - if (buffer != null) { - document.replace(firstOffset, 0, buffer.toString()); - } - } catch (BadLocationException e) { + } + } + + /** + * Process {@link #pendingPartitions}, append their content to document and + * update partitioning. + */ + private void processPendingPartitions() { + final List<PendingPartition> pendingCopy; + final int size; + synchronized (pendingPartitions) { + pendingCopy = new ArrayList<>(pendingPartitions); + size = pendingSize; + pendingPartitions.clear(); + pendingSize = 0; + pendingPartitions.notifyAll(); + } + synchronized (partitions) { + final StringBuilder addedContent = new StringBuilder(size); + IOConsolePartition lastPartition = getPartitionByIndex(partitions.size() - 1); + int nextOffset = document.getLength(); + for (PendingPartition pendingPartition : pendingCopy) { + if (lastPartition == null || lastPartition.getOutputStream() != pendingPartition.stream) { + lastPartition = new IOConsolePartition(nextOffset, pendingPartition.stream); + partitions.add(lastPartition); } - updatePartitions = null; - setUpdateInProgress(false); + final int pendingLength = pendingPartition.text.length(); + lastPartition.setLength(lastPartition.getLength() + pendingLength); + nextOffset += pendingLength; + addedContent.append(pendingPartition.text); } - if (consoleClosed) { - console.partitionerFinished(); + try { + updateType = DocUpdateType.OUTPUT; + document.replace(document.getLength(), 0, addedContent.toString()); + } catch (BadLocationException e) { + log(e); } - checkBufferSize(); } - + checkBufferSize(); + checkFinished(); } /** - * Job to trim the console document, runs in the UI thread. + * Job to trim the console document, runs in the UI thread. */ private class TrimJob extends WorkbenchJob { /** - * trims output up to the line containing the given offset, - * or all output if -1. + * Trims output up to the line containing the given offset, or all output if -1. */ private int truncateOffset; @@ -736,34 +793,31 @@ public class IOConsolePartitioner @Override public IStatus runInUIThread(IProgressMonitor monitor) { - if (document == null) { - return Status.OK_STATUS; - } + synchronized (partitions) { + if (document == null) { + return Status.OK_STATUS; + } - int length = document.getLength(); - if (truncateOffset < length) { - synchronized (overflowLock) { + int length = document.getLength(); + if (truncateOffset < length) { try { if (truncateOffset < 0) { // clear - setUpdateInProgress(true); + updateType = DocUpdateType.TRIM; document.set(""); //$NON-NLS-1$ - setUpdateInProgress(false); } else { // overflow int cutoffLine = document.getLineOfOffset(truncateOffset); int cutOffset = document.getLineOffset(cutoffLine); - // set the new length of the first partition IOConsolePartition partition = (IOConsolePartition) getPartition(cutOffset); partition.setLength(partition.getOffset() + partition.getLength() - cutOffset); - setUpdateInProgress(true); + updateType = DocUpdateType.TRIM; document.replace(0, cutOffset, ""); //$NON-NLS-1$ - setUpdateInProgress(false); - //remove partitions and reset Partition offsets + // remove partitions and reset Partition offsets int index = partitions.indexOf(partition); for (int i = 0; i < index; i++) { partitions.remove(0); @@ -794,9 +848,6 @@ public class IOConsolePartitioner @Override public StyleRange[] getStyleRanges(int offset, int length) { - if (!connected) { - return new StyleRange[0]; - } final IOConsolePartition[] computedPartitions = computeIOPartitioning(offset, length); final StyleRange[] styles = new StyleRange[computedPartitions.length]; for (int i = 0; i < computedPartitions.length; i++) { @@ -869,7 +920,7 @@ public class IOConsolePartitioner @Override public int getPreviousOffsetByState(int offset, boolean searchWritable) { - if (partitions != null) { + synchronized (partitions) { int partitionIndex = findPartitionCandidate(offset); for (; partitionIndex >= 0; partitionIndex--) { final IOConsolePartition partition = partitions.get(partitionIndex); @@ -883,7 +934,7 @@ public class IOConsolePartitioner @Override public int getNextOffsetByState(int offset, boolean searchWritable) { - if (partitions != null) { + synchronized (partitions) { int partitionIndex = findPartitionCandidate(offset); if (partitionIndex >= 0) { for (; partitionIndex < partitions.size(); partitionIndex++) { @@ -921,27 +972,33 @@ public class IOConsolePartitioner ConsolePlugin.log(t); } + private static void log(int status, String msg) { + ConsolePlugin.log(new Status(status, ConsolePlugin.getUniqueIdentifier(), msg)); + } + /** * For debug purpose. Check if whole document is partitioned, partitions are * ordered by offset, every partition has length greater 0 and all writable * input partitions are listed in {@link #inputPartitions}. */ private void checkPartitions() { - if (!connected) { + if (document == null) { return; } - final List<IOConsolePartition> knownInputPartitions = new ArrayList<>(inputPartitions); - int offset = 0; - for (IOConsolePartition partition : partitions) { - Assert.isTrue(offset == partition.getOffset()); - Assert.isTrue(partition.getLength() > 0); - offset += partition.getLength(); - - if (isInputPartition(partition) && !partition.isReadOnly()) { - Assert.isTrue(knownInputPartitions.remove(partition)); + synchronized (partitions) { + final List<IOConsolePartition> knownInputPartitions = new ArrayList<>(inputPartitions); + int offset = 0; + for (IOConsolePartition partition : partitions) { + Assert.isTrue(offset == partition.getOffset()); + Assert.isTrue(partition.getLength() > 0); + offset += partition.getLength(); + + if (isInputPartition(partition) && !partition.isReadOnly()) { + Assert.isTrue(knownInputPartitions.remove(partition)); + } } + Assert.isTrue(offset == document.getLength()); + Assert.isTrue(knownInputPartitions.isEmpty()); } - Assert.isTrue(offset == document.getLength()); - Assert.isTrue(knownInputPartitions.isEmpty()); } } |