blob: 0dad3d0270b412e3217dc37b52be53985f5c153a [file] [log] [blame]
/*
* Copyright 2000-2012 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.psi.formatter;
import com.intellij.formatting.FormattingDocumentModel;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.impl.DocumentImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.impl.PsiDocumentManagerImpl;
import com.intellij.psi.impl.PsiToDocumentSynchronizer;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class FormattingDocumentModelImpl implements FormattingDocumentModel {
private final WhiteSpaceFormattingStrategy myWhiteSpaceStrategy;
//private final CharBuffer myBuffer = CharBuffer.allocate(1);
@NotNull private final Document myDocument;
private final PsiFile myFile;
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.formatter.FormattingDocumentModelImpl");
private final CodeStyleSettings mySettings;
public FormattingDocumentModelImpl(@NotNull final Document document, PsiFile file) {
myDocument = document;
myFile = file;
if (file != null) {
Language language = file.getLanguage();
myWhiteSpaceStrategy = WhiteSpaceFormattingStrategyFactory.getStrategy(language);
}
else {
myWhiteSpaceStrategy = WhiteSpaceFormattingStrategyFactory.getStrategy();
}
mySettings = CodeStyleSettingsManager.getSettings(file != null ? file.getProject() : null);
}
public static FormattingDocumentModelImpl createOn(PsiFile file) {
Document document = getDocumentToBeUsedFor(file);
if (document != null) {
if (PsiDocumentManager.getInstance(file.getProject()).isUncommited(document)) {
LOG.error("Document is uncommitted");
}
if (!file.textMatches(document.getImmutableCharSequence())) {
LOG.error("Document and psi file texts should be equal: file " + file);
}
return new FormattingDocumentModelImpl(document, file);
}
else {
return new FormattingDocumentModelImpl(new DocumentImpl(file.getText()), file);
}
}
@Nullable
public static Document getDocumentToBeUsedFor(final PsiFile file) {
final Project project = file.getProject();
final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
if (document == null) return null;
if (PsiDocumentManager.getInstance(project).isUncommited(document)) return null;
PsiToDocumentSynchronizer synchronizer = ((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(file.getProject())).getSynchronizer();
if (synchronizer.isDocumentAffectedByTransactions(document)) return null;
return document;
}
@Override
public int getLineNumber(int offset) {
if (offset > myDocument.getTextLength()) {
LOG.error(String.format("Invalid offset detected (%d). Document length: %d. Target file: %s",
offset, myDocument.getTextLength(), myFile));
}
return myDocument.getLineNumber(offset);
}
@Override
public int getLineStartOffset(int line) {
return myDocument.getLineStartOffset(line);
}
@Override
public CharSequence getText(final TextRange textRange) {
if (textRange.getStartOffset() < 0 || textRange.getEndOffset() > myDocument.getTextLength()) {
LOG.error(String.format(
"Please submit a ticket to the tracker and attach current source file to it!%nInvalid processing detected: given text "
+ "range (%s) targets non-existing regions (the boundaries are [0; %d)). File's language: %s",
textRange, myDocument.getTextLength(), myFile.getLanguage()
));
}
return myDocument.getCharsSequence().subSequence(textRange.getStartOffset(), textRange.getEndOffset());
}
@Override
public int getTextLength() {
return myDocument.getTextLength();
}
@NotNull
@Override
public Document getDocument() {
return myDocument;
}
public PsiFile getFile() {
return myFile;
}
@Override
public boolean containsWhiteSpaceSymbolsOnly(int startOffset, int endOffset) {
WhiteSpaceFormattingStrategy strategy = myWhiteSpaceStrategy;
if (strategy.check(myDocument.getCharsSequence(), startOffset, endOffset) >= endOffset) {
return true;
}
PsiElement injectedElement = myFile != null ? InjectedLanguageUtil.findElementAtNoCommit(myFile, startOffset) : null;
if (injectedElement != null) {
Language injectedLanguage = injectedElement.getLanguage();
if (!injectedLanguage.equals(myFile.getLanguage())) {
WhiteSpaceFormattingStrategy localStrategy = WhiteSpaceFormattingStrategyFactory.getStrategy(injectedLanguage);
if (localStrategy != null) {
return localStrategy.check(myDocument.getCharsSequence(), startOffset, endOffset) >= endOffset;
}
}
}
return false;
}
@NotNull
@Override
public CharSequence adjustWhiteSpaceIfNecessary(@NotNull CharSequence whiteSpaceText, int startOffset, int endOffset,
ASTNode nodeAfter, boolean changedViaPsi)
{
if (!changedViaPsi) {
return myWhiteSpaceStrategy.adjustWhiteSpaceIfNecessary(whiteSpaceText, myDocument.getCharsSequence(), startOffset, endOffset,
mySettings, nodeAfter);
}
final PsiElement element = myFile.findElementAt(startOffset);
if (element == null) {
return whiteSpaceText;
}
else {
return myWhiteSpaceStrategy.adjustWhiteSpaceIfNecessary(whiteSpaceText, element, startOffset, endOffset, mySettings);
}
}
//@Override
//public boolean isWhiteSpaceSymbol(char symbol) {
// myBuffer.put(0, symbol);
// return myWhiteSpaceStrategy.check(myBuffer, 0, 1) > 0;
//}
public static boolean canUseDocumentModel(@NotNull Document document,@NotNull PsiFile file) {
PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(file.getProject());
return !psiDocumentManager.isUncommited(document) &&
!psiDocumentManager.isDocumentBlockedByPsi(document) &&
file.getText().equals(document.getText());
}
}