package com.intellij.lang.javascript.flex.projectStructure.detection;
import com.intellij.ide.util.DelegatingProgressIndicator;
import com.intellij.ide.util.importProject.LibrariesDetectionStep;
import com.intellij.ide.util.importProject.ModulesDetectionStep;
import com.intellij.ide.util.importProject.ProjectDescriptor;
import com.intellij.ide.util.projectWizard.ModuleWizardStep;
import com.intellij.ide.util.projectWizard.importSources.DetectedProjectRoot;
import com.intellij.ide.util.projectWizard.importSources.ProjectFromSourcesBuilder;
import com.intellij.ide.util.projectWizard.importSources.ProjectStructureDetector;
import com.intellij.ide.util.projectWizard.importSources.util.CommonSourceRootDetectionUtil;
import com.intellij.lang.LanguageParserDefinitions;
import com.intellij.lang.javascript.ActionScriptFileType;
import com.intellij.lang.javascript.JSTokenTypes;
import com.intellij.lang.javascript.JavaScriptSupportLoader;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.NullableFunction;
import com.intellij.util.StringBuilderSpinAllocator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class FlexProjectStructureDetector extends ProjectStructureDetector {
public static final NullableFunction<CharSequence, String> PACKAGE_NAME_FETCHER = charSequence -> {
Lexer lexer = LanguageParserDefinitions.INSTANCE.forLanguage(JavaScriptSupportLoader.ECMA_SCRIPT_L4).createLexer(null);
lexer.start(charSequence);
return readPackageName(charSequence, lexer);
};
public static boolean isActionScriptFile(File file) {
return FileTypeRegistry.getInstance().getFileTypeByFileName(file.getName()) == ActionScriptFileType.INSTANCE;
}
@NotNull
public DirectoryProcessingResult detectRoots(@NotNull final File dir,
@NotNull final File[] children,
@NotNull final File base,
@NotNull final List<DetectedProjectRoot> result) {
for (File child : children) {
if (child.isFile()) {
if (isActionScriptFile(child)) {
Pair<File, String> root =
CommonSourceRootDetectionUtil.IO_FILE.suggestRootForFileWithPackageStatement(child, base, PACKAGE_NAME_FETCHER, false);
if (root != null) {
result.add(new FlexModuleSourceRoot(root.getFirst()));
return DirectoryProcessingResult.skipChildrenAndParentsUpTo(root.getFirst());
}
else {
return DirectoryProcessingResult.SKIP_CHILDREN;
}
}
else if (JavaScriptSupportLoader.isFlexMxmFile(child.getName())) {
result.add(new FlexModuleSourceRoot(dir));
// don't skip this folder since .as files can be located here, and they will make some parent folder marked as source root
// essentially 'dir' will be marked as source root *only* if no .as files exist at this level or below
}
}
if ("node_modules".equals(child.getName())) {
return DirectoryProcessingResult.SKIP_CHILDREN;
}
}
return DirectoryProcessingResult.PROCESS_CHILDREN;
}
public List<ModuleWizardStep> createWizardSteps(final ProjectFromSourcesBuilder builder,
final ProjectDescriptor projectDescriptor,
final Icon stepIcon) {
FlexModuleInsight moduleInsight =
new FlexModuleInsight(new DelegatingProgressIndicator(), builder.getExistingModuleNames(), builder.getExistingProjectLibraryNames());
final List<ModuleWizardStep> steps = new ArrayList<>();
steps.add(
new LibrariesDetectionStep(builder, projectDescriptor, moduleInsight, stepIcon, "reference.dialogs.new.project.fromCode.page1"));
steps.add(
new ModulesDetectionStep(this, builder, projectDescriptor, moduleInsight, stepIcon, "reference.dialogs.new.project.fromCode.page2"));
steps.add(new FlexSdkStep(builder.getContext()));
return steps;
}
@Nullable
public static String readPackageName(final CharSequence charSequence, final Lexer lexer) {
skipWhiteSpaceAndComments(lexer);
if (!JSTokenTypes.PACKAGE_KEYWORD.equals(lexer.getTokenType())) {
return null;
}
lexer.advance();
skipWhiteSpaceAndComments(lexer);
return readQualifiedName(charSequence, lexer, false);
}
@Nullable
static String readQualifiedName(final CharSequence charSequence, final Lexer lexer, boolean allowStar) {
final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
try {
while (true) {
if (lexer.getTokenType() != JSTokenTypes.IDENTIFIER && !(allowStar && lexer.getTokenType() != JSTokenTypes.MULT)) break;
buffer.append(charSequence, lexer.getTokenStart(), lexer.getTokenEnd());
if (lexer.getTokenType() == JSTokenTypes.MULT) break;
lexer.advance();
if (lexer.getTokenType() != JSTokenTypes.DOT) break;
buffer.append('.');
lexer.advance();
}
String packageName = buffer.toString();
if (StringUtil.endsWithChar(packageName, '.')) return null;
return packageName;
}
finally {
StringBuilderSpinAllocator.dispose(buffer);
}
}
private static final TokenSet WHITESPACE_AND_COMMENTS =
TokenSet.create(JSTokenTypes.WHITE_SPACE, JSTokenTypes.DOC_COMMENT, JSTokenTypes.C_STYLE_COMMENT, JSTokenTypes.END_OF_LINE_COMMENT);
public static void skipWhiteSpaceAndComments(Lexer lexer) {
while (WHITESPACE_AND_COMMENTS.contains(lexer.getTokenType())) {
lexer.advance();
}
}
}