From 02cf98d65c798d368fcec43ed64a001d513bdd4f Mon Sep 17 00:00:00 2001 From: Tor Norbye Date: Tue, 19 Aug 2014 12:53:10 -0700 Subject: Snapshot idea/138.1503 from git://git.jetbrains.org/idea/community.git Change-Id: Ie01af1d8710ec0ff51d90301bda1a18b0b5c0faf --- .../editor/impl/LazyRangeMarkerFactoryImpl.java | 323 +++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java (limited to 'platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java') diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java new file mode 100644 index 000000000000..5a9c704173aa --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java @@ -0,0 +1,323 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.editor.impl; + +import com.intellij.codeStyle.CodeStyleFacade; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.LazyRangeMarkerFactory; +import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.editor.event.DocumentAdapter; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.WeakList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class LazyRangeMarkerFactoryImpl extends LazyRangeMarkerFactory { + private final Project myProject; + private static final Key> LAZY_MARKERS_KEY = Key.create("LAZY_MARKERS_KEY"); + + public LazyRangeMarkerFactoryImpl(@NotNull Project project, @NotNull final FileDocumentManager fileDocumentManager) { + myProject = project; + + EditorFactory.getInstance().getEventMulticaster().addDocumentListener(new DocumentAdapter() { + @Override + public void beforeDocumentChange(DocumentEvent e) { + transformRangeMarkers(e); + } + + @Override + public void documentChanged(DocumentEvent e) { + transformRangeMarkers(e); + } + + private void transformRangeMarkers(@NotNull DocumentEvent e) { + Document document = e.getDocument(); + VirtualFile file = fileDocumentManager.getFile(document); + if (file == null) { + return; + } + + WeakList lazyMarkers = getMarkers(file); + if (lazyMarkers == null) { + return; + } + + List markers = lazyMarkers.toStrongList(); + for (LazyMarker marker : markers) { + if (file.equals(marker.getFile())) { + marker.getOrCreateDelegate(); + } + } + } + }, project); + } + + static WeakList getMarkers(@NotNull VirtualFile file) { + return file.getUserData(LazyRangeMarkerFactoryImpl.LAZY_MARKERS_KEY); + } + + private static void addToLazyMarkersList(@NotNull LazyMarker marker, @NotNull VirtualFile file) { + WeakList markers = getMarkers(file); + + if (markers == null) { + markers = file.putUserDataIfAbsent(LAZY_MARKERS_KEY, new WeakList()); + } + markers.add(marker); + } + + private static void removeFromLazyMarkersList(@NotNull LazyMarker marker, @NotNull VirtualFile file) { + WeakList markers = getMarkers(file); + + if (markers != null) { + markers.remove(marker); + } + } + + @Override + @NotNull + public RangeMarker createRangeMarker(@NotNull final VirtualFile file, final int offset) { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public RangeMarker compute() { + // even for already loaded document do not create range marker yet - wait until it really needed when e.g. user clicked to jump to OpenFileDescriptor + final LazyMarker marker = new OffsetLazyMarker(file, offset); + addToLazyMarkersList(marker, file); + return marker; + } + }); + } + + @Override + @NotNull + public RangeMarker createRangeMarker(@NotNull final VirtualFile file, final int line, final int column, final boolean persistent) { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public RangeMarker compute() { + final Document document = FileDocumentManager.getInstance().getCachedDocument(file); + if (document != null) { + final int offset = calculateOffset(myProject, file, document, line, column); + return document.createRangeMarker(offset, offset, persistent); + } + + final LazyMarker marker = new LineColumnLazyMarker(file, line, column); + addToLazyMarkersList(marker, file); + return marker; + } + }); + } + + abstract static class LazyMarker extends UserDataHolderBase implements RangeMarker { + protected RangeMarker myDelegate; // the real range marker which is created only when document is opened, or (this) which means it's disposed + protected final VirtualFile myFile; + protected final int myInitialOffset; + + private LazyMarker(@NotNull VirtualFile file, int offset) { + myFile = file; + myInitialOffset = offset; + } + + boolean isDelegated() { + return myDelegate != null; + } + + @NotNull + public VirtualFile getFile() { + return myFile; + } + + @Nullable + protected final RangeMarker getOrCreateDelegate() { + if (myDelegate == null) { + Document document = FileDocumentManager.getInstance().getDocument(myFile); + if (document == null) { + return null; + } + myDelegate = createDelegate(myFile, document); + removeFromLazyMarkersList(this, myFile); + } + return isDisposed() ? null : myDelegate; + } + + @Nullable + protected abstract RangeMarker createDelegate(@NotNull VirtualFile file, @NotNull Document document); + + @Override + @NotNull + public Document getDocument() { + RangeMarker delegate = getOrCreateDelegate(); + if (delegate == null) { + //noinspection ConstantConditions + return FileDocumentManager.getInstance().getDocument(myFile); + } + return delegate.getDocument(); + } + + @Override + public int getStartOffset() { + return myDelegate == null || isDisposed() ? myInitialOffset : myDelegate.getStartOffset(); + } + + public boolean isDisposed() { + return myDelegate == this; + } + + + @Override + public int getEndOffset() { + return myDelegate == null || isDisposed() ? myInitialOffset : myDelegate.getEndOffset(); + } + + @Override + public boolean isValid() { + RangeMarker delegate = getOrCreateDelegate(); + return delegate != null && !isDisposed() && delegate.isValid(); + } + + @Override + public void setGreedyToLeft(boolean greedy) { + getOrCreateDelegate().setGreedyToLeft(greedy); + } + + @Override + public void setGreedyToRight(boolean greedy) { + getOrCreateDelegate().setGreedyToRight(greedy); + } + + @Override + public boolean isGreedyToRight() { + return getOrCreateDelegate().isGreedyToRight(); + } + + @Override + public boolean isGreedyToLeft() { + return getOrCreateDelegate().isGreedyToLeft(); + } + + @Override + public void dispose() { + assert !isDisposed(); + RangeMarker delegate = myDelegate; + if (delegate == null) { + removeFromLazyMarkersList(this, myFile); + myDelegate = this; // mark of disposed marker + } + else { + delegate.dispose(); + } + } + } + + private static class OffsetLazyMarker extends LazyMarker { + private OffsetLazyMarker(@NotNull VirtualFile file, int offset) { + super(file, offset); + } + + @Override + public boolean isValid() { + RangeMarker delegate = myDelegate; + if (delegate == null) { + Document document = FileDocumentManager.getInstance().getDocument(myFile); + return document != null; + } + + return super.isValid(); + } + + @Override + @NotNull + public RangeMarker createDelegate(@NotNull VirtualFile file, @NotNull final Document document) { + final int offset = Math.min(myInitialOffset, document.getTextLength()); + return document.createRangeMarker(offset, offset); + } + } + + private class LineColumnLazyMarker extends LazyMarker { + private final int myLine; + private final int myColumn; + + private LineColumnLazyMarker(@NotNull VirtualFile file, int line, int column) { + super(file, -1); + myLine = line; + myColumn = column; + } + + @Override + @Nullable + public RangeMarker createDelegate(@NotNull VirtualFile file, @NotNull Document document) { + if (document.getTextLength() == 0 && !(myLine == 0 && myColumn == 0)) { + return null; + } + + int offset = calculateOffset(myProject, file, document, myLine, myColumn); + return document.createRangeMarker(offset, offset); + } + + @Override + public boolean isValid() { + RangeMarker delegate = myDelegate; + if (delegate == null) { + Document document = FileDocumentManager.getInstance().getDocument(myFile); + return document != null && (document.getTextLength() != 0 || myLine == 0 && myColumn == 0); + } + + return super.isValid(); + } + + @Override + public int getStartOffset() { + getOrCreateDelegate(); + return super.getStartOffset(); + } + + @Override + public int getEndOffset() { + getOrCreateDelegate(); + return super.getEndOffset(); + } + } + + private static int calculateOffset(@NotNull Project project, @NotNull VirtualFile file, @NotNull Document document, final int line, final int column) { + int offset; + if (line < document.getLineCount()) { + final int lineStart = document.getLineStartOffset(line); + final int lineEnd = document.getLineEndOffset(line); + final CharSequence docText = document.getCharsSequence(); + final int tabSize = CodeStyleFacade.getInstance(project).getTabSize(file.getFileType()); + + offset = lineStart; + int col = 0; + while (offset < lineEnd && col < column) { + col += docText.charAt(offset) == '\t' ? tabSize : 1; + offset++; + } + } + else { + offset = document.getTextLength(); + } + return offset; + } + +} -- cgit v1.2.3