Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.siberika.idea.pascal.lang;

import com.siberika.idea.pascal.PascalBundle;
import com.siberika.idea.pascal.PascalFileType;
import com.siberika.idea.pascal.PascalLanguage;
import com.siberika.idea.pascal.lang.context.ContextUtil;
Expand All @@ -10,6 +9,7 @@
import com.siberika.idea.pascal.util.DocUtil;
import com.siberika.idea.pascal.util.ModuleUtil;
import com.siberika.idea.pascal.util.PsiUtil;
import consulo.annotation.access.RequiredReadAction;
import consulo.annotation.component.ExtensionImpl;
import consulo.document.Document;
import consulo.document.util.TextRange;
Expand All @@ -21,26 +21,25 @@
import consulo.language.psi.PsiFile;
import consulo.language.psi.PsiManager;
import consulo.language.psi.util.PsiTreeUtil;
import consulo.language.util.ModuleUtilCore;
import consulo.localize.LocalizeValue;
import consulo.logging.Logger;
import consulo.module.Module;
import consulo.object.pascal.localize.ObjectPascalLocalize;
import consulo.project.Project;
import consulo.util.collection.SmartList;
import consulo.util.lang.Pair;
import jakarta.annotation.Nonnull;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.regex.Pattern;

/**
* Author: George Bakhtadze
* Date: 17/12/2015
* @author George Bakhtadze
* @since 2015-12-17
*/
@ExtensionImpl
public class PascalImportOptimizer implements ImportOptimizer {

private static final Logger LOG = Logger.getInstance(PascalImportOptimizer.class);

private static final Pattern RE_UNITNAME_PREFIX = Pattern.compile("[{}!]");
Expand All @@ -60,14 +59,17 @@ public static UsedUnitStatus getUsedUnitStatus(PascalQualifiedIdent usedUnitName
}
PascalModule pasModule = PsiUtil.getElementPasModule(usedUnitName);
if ((pasModule != null)) {
Pair<List<PascalNamedElement>, List<PascalNamedElement>> idents = pasModule.getIdentsFrom(usedUnitName.getName(), true, ModuleUtil.retrieveUnitNamespaces(module, project));
Pair<List<PascalNamedElement>, List<PascalNamedElement>> idents =
pasModule.getIdentsFrom(usedUnitName.getName(), true, ModuleUtil.retrieveUnitNamespaces(module, project));
if (ContextUtil.belongsToInterface(usedUnitName)) {
if (idents.getFirst().size() + idents.getSecond().size() == 0) {
res = UsedUnitStatus.UNUSED;
} else if (idents.getFirst().size() == 0) {
}
else if (idents.getFirst().size() == 0) {
res = UsedUnitStatus.USED_IN_IMPL;
}
} else if (idents.getSecond().size() == 0) {
}
else if (idents.getSecond().size() == 0) {
res = UsedUnitStatus.UNUSED;
}
}
Expand All @@ -85,17 +87,22 @@ private static boolean supportsOptimization(PsiFile file) {

@NotNull
@Override
@RequiredReadAction
public Runnable processFile(final PsiFile file) {
return doProcess(file);
}

@RequiredReadAction
public static Runnable doProcess(final PsiFile file) {
final Map<PascalQualifiedIdent, UsedUnitStatus> units = new TreeMap<PascalQualifiedIdent, UsedUnitStatus>(new ByOffsetComparator<PascalQualifiedIdent>());
Map<PascalQualifiedIdent, UsedUnitStatus> units = new TreeMap<>(new ByOffsetComparator<>());
Collection<PasUsesClause> usesClauses = PsiTreeUtil.findChildrenOfType(file, PasUsesClause.class);

Module module = ModuleUtilCore.findModuleForPsiElement(file);
Module module = file.getModule();
//noinspection unchecked
for (PascalQualifiedIdent usedUnitName : PsiUtil.findChildrenOfAnyType(PsiUtil.getElementPasModule(file), PascalQualifiedIdent.class)) {
for (PascalQualifiedIdent usedUnitName : PsiUtil.findChildrenOfAnyType(
PsiUtil.getElementPasModule(file),
PascalQualifiedIdent.class
)) {
if (PsiUtil.isUsedUnitName(usedUnitName)) {
UsedUnitStatus status = PascalImportOptimizer.getUsedUnitStatus(usedUnitName, module);
if (status != UsedUnitStatus.USED) {
Expand All @@ -109,7 +116,8 @@ public static Runnable doProcess(final PsiFile file) {
for (PasUsesClause usesClause : usesClauses) {
if (ContextUtil.belongsToInterface(usesClause)) {
usesIntf = usesClause;
} else {
}
else {
usesImpl = usesClause;
}
}
Expand All @@ -119,13 +127,11 @@ public static Runnable doProcess(final PsiFile file) {
final Document doc = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);

return new CollectingInfoRunnable() {
private LocalizeValue myStatus;

private String status;

@Nullable
@Override
public String getUserNotificationInfo() {
return status;
public LocalizeValue getUserNotificationInfo() {
return myStatus;
}

@Override
Expand All @@ -136,13 +142,13 @@ public void run() {
try {
int remIntf = usesInterface != null ? usesInterface.getNamespaceIdentList().size() : 0;
int remImpl = usesImplementation != null ? usesImplementation.getNamespaceIdentList().size() : 0;
List<TextRange> toRemoveIntf = new SmartList<TextRange>();
List<TextRange> toRemoveImpl = new SmartList<TextRange>();
List<TextRange> toRemoveIntf = new SmartList<>();
List<TextRange> toRemoveImpl = new SmartList<>();
List<TextRange> unitRangesIntf = getUnitRanges(usesInterface);
List<TextRange> unitRangesImpl = getUnitRanges(usesImplementation);
List<String> toMove = new SmartList<String>();
for (Map.Entry<PascalQualifiedIdent, UsedUnitStatus> unit : units.entrySet()) { // perform add operations
if (unit.getValue() == UsedUnitStatus.USED_IN_IMPL) { // move from interface to implementation
List<String> toMove = new SmartList<>();
for (Map.Entry<PascalQualifiedIdent, UsedUnitStatus> unit : units.entrySet()) { // perform add operations
if (unit.getValue() == UsedUnitStatus.USED_IN_IMPL) { // move from interface to implementation
toMove.add(unit.getKey().getName());
remImpl++;
}
Expand All @@ -152,92 +158,116 @@ public void run() {
unitRangesImpl.add(addedRange);
}
int unknown = 0;
for (Map.Entry<PascalQualifiedIdent, UsedUnitStatus> unit : units.entrySet()) { // collect all removal ranges
if (unit.getValue() == UsedUnitStatus.USED_IN_IMPL) { // remove due to moving to implementation
for (Map.Entry<PascalQualifiedIdent, UsedUnitStatus> unit : units.entrySet()) { // collect all removal ranges
if (unit.getValue() == UsedUnitStatus.USED_IN_IMPL) { // remove due to moving to implementation
TextRange range = removeUnitFromSection(unit.getKey(), usesInterface, unitRangesIntf, remIntf);
if (range != null) {
toRemoveIntf.add(range);
remIntf--;
}
} else if (unit.getValue() == UsedUnitStatus.UNUSED) {
}
else if (unit.getValue() == UsedUnitStatus.UNUSED) {
TextRange range = removeUnitFromSection(unit.getKey(), usesInterface, unitRangesIntf, remIntf);
if (range != null) {
remIntf--;
toRemoveIntf.add(range);
} else {
}
else {
range = removeUnitFromSection(unit.getKey(), usesImplementation, unitRangesImpl, remImpl);
if (range != null) {
remImpl--;
toRemoveImpl.add(range);
}
}
} else if (unit.getValue() == UsedUnitStatus.UNKNOWN) {
}
else if (unit.getValue() == UsedUnitStatus.UNKNOWN) {
unknown++;
}
}
removeUnits(doc, usesImplementation, toRemoveImpl, remImpl); // remove implementation uses clause before other modifications
removeUnits(
doc,
usesImplementation,
toRemoveImpl,
remImpl
); // remove implementation uses clause before other modifications
removeUnits(doc, usesInterface, toRemoveIntf, remIntf);
PsiDocumentManager.getInstance(file.getProject()).commitDocument(doc);
status = String.format("%d units moved to implementation, %d removed, %d unknown",
toMove.size(), toRemoveImpl.size() + toRemoveIntf.size() - toMove.size(), unknown);
} catch (Exception e) {
myStatus = LocalizeValue.localizeTODO(String.format(
"%d units moved to implementation, %d removed, %d unknown",
toMove.size(), toRemoveImpl.size() + toRemoveIntf.size() - toMove.size(), unknown
));
}
catch (Exception e) {
LOG.info("Error", e);
}
}
};
}

@RequiredReadAction
private static void removeUnits(Document doc, PasUsesClause clause, List<TextRange> toRemove, int remaining) {
if ((clause != null) && (0 == remaining)) {
doc.deleteString(clause.getTextRange().getStartOffset(), DocUtil.expandRangeEnd(doc, clause.getTextRange().getEndOffset(), DocUtil.RE_LF));
} else {
if (clause != null && 0 == remaining) {
doc.deleteString(
clause.getTextRange().getStartOffset(),
DocUtil.expandRangeEnd(doc, clause.getTextRange().getEndOffset(), DocUtil.RE_LF)
);
}
else {
Collections.sort(toRemove, new ByOffsetComparator2());
for (TextRange textRange : toRemove) {
doc.deleteString(textRange.getStartOffset(), textRange.getEndOffset());
}
}
}

@RequiredReadAction
public static List<TextRange> getUnitRanges(PasUsesClause usesClause) {
if (null == usesClause) {
return new SmartList<TextRange>();
return new SmartList<>();
}
List<TextRange> res = new ArrayList<TextRange>(usesClause.getNamespaceIdentList().size());
List<TextRange> res = new ArrayList<>(usesClause.getNamespaceIdentList().size());
for (PascalQualifiedIdent ident : usesClause.getNamespaceIdentList()) {
res.add(ident.getTextRange());
}
return res;
}

@RequiredReadAction
public static TextRange addUnitToSection(final PasModule module, List<String> names, boolean toInterface) {
if ((null == module) || (names.isEmpty())) {
if (null == module || names.isEmpty()) {
return null;
}
assert (!toInterface || (module.getModuleType() == PascalModule.ModuleType.UNIT));
assert (!toInterface || module.getModuleType() == PascalModule.ModuleType.UNIT);
final PasUsesClause uses;
if (toInterface) {
uses = PsiTreeUtil.findChildOfType(PsiUtil.getModuleInterfaceSection(module), PasUsesClause.class);
} else {
uses = PsiTreeUtil.findChildOfType((module.getModuleType() == PascalModule.ModuleType.UNIT) ? PsiUtil.getModuleImplementationSection(module) : module, PasUsesClause.class);
}
else {
uses = PsiTreeUtil.findChildOfType(
module.getModuleType() == PascalModule.ModuleType.UNIT ? PsiUtil.getModuleImplementationSection(module) : module,
PasUsesClause.class
);
}
int offs = 0;
String content = String.join(", ", names);
if (uses != null) {
offs = uses.getTextRange().getEndOffset() - 1;
content = ",\n" + content + ";";
} else {
}
else {
content = "\n\nuses\n" + content + ";\n";
@SuppressWarnings("unchecked") PsiElement prev = PsiTreeUtil.findChildOfAnyType(module, PasProgramModuleHead.class, PasLibraryModuleHead.class, PasPackageModuleHead.class);
@SuppressWarnings("unchecked") PsiElement prev =
PsiTreeUtil.findChildOfAnyType(module, PasProgramModuleHead.class, PasLibraryModuleHead.class, PasPackageModuleHead.class);
if (calcOffset(prev) >= 0) {
offs = calcOffset(prev);
} else {
if (toInterface) {
PsiElement section = PsiUtil.getModuleInterfaceSection(module);
offs = section != null ? section.getTextRange().getStartOffset() + "interface".length() : offs;
} else {
PsiElement section = PsiUtil.getModuleImplementationSection(module);
offs = section != module ? section.getTextRange().getStartOffset() + "implementation".length(): offs;
}
}
else if (toInterface) {
PsiElement section = PsiUtil.getModuleInterfaceSection(module);
offs = section != null ? section.getTextRange().getStartOffset() + "interface".length() : offs;
}
else {
PsiElement section = PsiUtil.getModuleImplementationSection(module);
offs = section != module ? section.getTextRange().getStartOffset() + "implementation".length() : offs;
}
}
final PsiFile file = module.getContainingFile();
Expand All @@ -246,26 +276,38 @@ public static TextRange addUnitToSection(final PasModule module, List<String> na
DocUtil.adjustDocument(doc, offs, content);
PsiDocumentManager.getInstance(module.getProject()).commitDocument(doc);
}
DocUtil.runCommandLaterInWriteAction(module.getProject(), PascalBundle.message("action.reformat"), new Runnable() {
@Override
public void run() {
DocUtil.runCommandLaterInWriteAction(
module.getProject(),
ObjectPascalLocalize.actionReformat().get(),
() -> {
for (PasUsesClause usesClause : PsiTreeUtil.findChildrenOfType(module, PasUsesClause.class)) {
PsiManager manager = usesClause.getManager();
if (manager != null) {
CodeStyleManager.getInstance(manager).reformatRange(file, usesClause.getTextRange().getStartOffset(), usesClause.getTextRange().getEndOffset(), true);
CodeStyleManager.getInstance(manager).reformatRange(
file,
usesClause.getTextRange().getStartOffset(),
usesClause.getTextRange().getEndOffset(),
true
);
}
}
}
});
);
return TextRange.create(offs + 2, offs + content.length());
}

@RequiredReadAction
private static int calcOffset(PsiElement prev) {
return prev != null ? prev.getTextRange().getEndOffset() : -1;
}

public static TextRange removeUnitFromSection(PascalQualifiedIdent usedUnit, PasUsesClause uses, List<TextRange> unitRanges, int remaining) {
if ((0 == remaining) || (null == uses) || (null == uses.getContainingFile())) {
public static TextRange removeUnitFromSection(
PascalQualifiedIdent usedUnit,
PasUsesClause uses,
List<TextRange> unitRanges,
int remaining
) {
if (0 == remaining || null == uses || null == uses.getContainingFile()) {
return null;
}
Document doc = PsiDocumentManager.getInstance(uses.getProject()).getDocument(uses.getContainingFile());
Expand All @@ -274,21 +316,19 @@ public static TextRange removeUnitFromSection(PascalQualifiedIdent usedUnit, Pas
return null;
}
int start = DocUtil.expandRangeStart(doc, unitRanges.get(index).getStartOffset(), RE_UNITNAME_PREFIX);
int end = unitRanges.get(index).getEndOffset(); // Single
int end = unitRanges.get(index).getEndOffset(); // Single

if (index > 0) {
if (index == remaining - 1) { // Right
if (index == remaining - 1) { // Right
start = unitRanges.get(index - 1).getEndOffset();
} else {
if (index < remaining - 1) { // Middle
end = DocUtil.expandRangeStart(doc, unitRanges.get(index + 1).getStartOffset(), RE_UNITNAME_PREFIX);
}
}
} else {
if (index < unitRanges.size() - 1) { // Left
else if (index < remaining - 1) { // Middle
end = DocUtil.expandRangeStart(doc, unitRanges.get(index + 1).getStartOffset(), RE_UNITNAME_PREFIX);
}
}
else if (index < unitRanges.size() - 1) { // Left
end = DocUtil.expandRangeStart(doc, unitRanges.get(index + 1).getStartOffset(), RE_UNITNAME_PREFIX);
}
return TextRange.create(start, end);
}

Expand All @@ -312,6 +352,7 @@ public Language getLanguage() {

private static class ByOffsetComparator<T extends PsiElement> implements Comparator<T> {
@Override
@RequiredReadAction
public int compare(PsiElement o1, PsiElement o2) {
return o2.getTextRange().getStartOffset() - o1.getTextRange().getStartOffset();
}
Expand All @@ -323,6 +364,4 @@ public int compare(TextRange o1, TextRange o2) {
return o2.getStartOffset() - o1.getStartOffset();
}
}


}
Loading