Initial version.

This commit is contained in:
rikvdkleij@gmail.com 2014-05-12 10:30:22 +02:00
commit a737fbc1e1
40 changed files with 2079 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
/gen
/dist
/out
/target
/.idea/workspace.xml
/.idea/vcs.xml
/.idea/rik.xml
/.idea/dictionaries/rik.xml
*~

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
intellij-haskell

23
.idea/compiler.xml Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Javac" />
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

5
.idea/encodings.xml Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>

8
.idea/highlighting.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="HighlightingAdvisor">
<option name="SUGGEST_TYPE_AWARE_HIGHLIGHTING" value="false" />
<option name="TYPE_AWARE_HIGHLIGHTING_ENABLED" value="true" />
</component>
</project>

View File

@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="scala-compiler">
<CLASSES>
<root url="file:///usr/share/scala/lib" />
</CLASSES>
<JAVADOC />
<SOURCES />
<jarDirectory url="file:///usr/share/scala/lib" recursive="false" />
</library>
</component>

View File

@ -0,0 +1,21 @@
<component name="libraryTable">
<library name="scala-library">
<CLASSES>
<root url="jar:///usr/share/scala/lib/akka-actor_2.11-2.3.2.jar!/" />
<root url="jar:///usr/share/scala/lib/config-1.2.0.jar!/" />
<root url="jar:///usr/share/scala/lib/jline-2.11.jar!/" />
<root url="jar:///usr/share/scala/lib/scala-actors-2.11.0.jar!/" />
<root url="jar:///usr/share/scala/lib/scala-compiler.jar!/" />
<root url="jar:///usr/share/scala/lib/scala-continuations-library_2.11-1.0.1.jar!/" />
<root url="jar:///usr/share/scala/lib/scala-library.jar!/" />
<root url="jar:///usr/share/scala/lib/scala-parser-combinators_2.11-1.0.1.jar!/" />
<root url="jar:///usr/share/scala/lib/scala-reflect.jar!/" />
<root url="jar:///usr/share/scala/lib/scala-swing_2.11-1.0.1.jar!/" />
<root url="jar:///usr/share/scala/lib/scala-xml_2.11-1.0.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/scala/scala-library-2.11.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="scalatest">
<CLASSES>
<root url="jar://$USER_HOME$/scala/scalatest/scalatest_2.11-2.1.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/scala/scalatest/scalatest_2.11-2.1.5-sources.jar!/" />
</SOURCES>
</library>
</component>

10
.idea/misc.xml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA IU-135.690" project-jdk-type="IDEA JDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

9
.idea/modules.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/intellij-haskell.iml" filepath="$PROJECT_DIR$/intellij-haskell.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,5 @@
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>

125
.idea/uiDesigner.xml Normal file
View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

46
META-INF/plugin.xml Normal file
View File

@ -0,0 +1,46 @@
<idea-plugin version="2">
<id>com.powertuple.intellij.haskell</id>
<name>Haskell plugin for IntelliJ</name>
<version>0.1</version>
<vendor email="rikvdkleij@gmail.com" url="http://www.powertuple.com">PowerTuple</vendor>
<description><![CDATA[
Haskell support.
]]></description>
<change-notes><![CDATA[
First version.
]]>
</change-notes>
<!-- please see http://confluence.jetbrains.com/display/IDEADEV/Build+Number+Ranges for description -->
<idea-version since-build="135"/>
<!-- please see http://confluence.jetbrains.com/display/IDEADEV/Plugin+Compatibility+with+IntelliJ+Platform+Products
on how to target different products -->
<!-- uncomment to enable plugin in all products
<depends>com.intellij.modules.lang</depends>
-->
<extensions defaultExtensionNs="com.intellij">
<fileTypeFactory implementation="com.powertuple.intellij.haskell.HaskellFileTypeFactory"/>
<lang.parserDefinition language="Haskell" implementationClass="com.powertuple.intellij.haskell.HaskellParserDefinition"/>
<lang.syntaxHighlighterFactory key="Haskell"
implementationClass="com.powertuple.intellij.haskell.highlighter.HaskellSyntaxHighlighterFactory"/>
<externalAnnotator language="Haskell" implementationClass="com.powertuple.intellij.haskell.annotator.GhcModExternalAnnotator"/>
</extensions>
<application-components>
<!-- Add your application components here -->
</application-components>
<project-components>
<!-- Add your project components here -->
</project-components>
<actions>
<!-- Add your actions here -->
</actions>
</idea-plugin>

1
README.md Normal file
View File

@ -0,0 +1 @@
IntelliJ plugin for Haskell

27
intellij-haskell.iml Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PLUGIN_MODULE" version="4">
<component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/META-INF/plugin.xml" />
<component name="FacetManager">
<facet type="scala" name="Scala">
<configuration>
<option name="compilerLibraryLevel" value="Project" />
<option name="compilerLibraryName" value="scala-compiler" />
<option name="languageLevel" value="Scala 2.11" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="scala-library" level="project" />
<orderEntry type="library" name="scalatest" level="project" />
</component>
</module>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

View File

@ -0,0 +1,39 @@
package com.powertuple.intellij.haskell;
import com.intellij.openapi.fileTypes.LanguageFileType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class HaskellFileType extends LanguageFileType {
public static final HaskellFileType INSTANCE = new HaskellFileType();
private HaskellFileType() {
super(com.powertuple.intellij.haskell.HaskellLanguage.INSTANCE);
}
@NotNull
@Override
public String getName() {
return "Haskell file";
}
@NotNull
@Override
public String getDescription() {
return "Haskell language file";
}
@NotNull
@Override
public String getDefaultExtension() {
return "hs";
}
@Nullable
@Override
public Icon getIcon() {
return HaskellIcons.HASKELL_SMALL_LOGO;
}
}

View File

@ -0,0 +1,12 @@
package com.powertuple.intellij.haskell;
import com.intellij.openapi.fileTypes.FileTypeConsumer;
import com.intellij.openapi.fileTypes.FileTypeFactory;
import org.jetbrains.annotations.NotNull;
public class HaskellFileTypeFactory extends FileTypeFactory {
@Override
public void createFileTypes(@NotNull FileTypeConsumer consumer) {
consumer.consume(HaskellFileType.INSTANCE, "Haskell");
}
}

View File

@ -0,0 +1,9 @@
package com.powertuple.intellij.haskell;
import com.intellij.openapi.util.IconLoader;
import javax.swing.*;
public class HaskellIcons {
public static final Icon HASKELL_SMALL_LOGO = IconLoader.getIcon("/icons/haskell-small-logo.png");
}

View File

@ -0,0 +1,21 @@
package com.powertuple.intellij.haskell;
import com.intellij.lang.Language;
public class HaskellLanguage extends Language {
public static final HaskellLanguage INSTANCE = new HaskellLanguage();
public HaskellLanguage() {
super("Haskell");
}
@Override
public String getDisplayName() {
return "Haskell language";
}
@Override
public boolean isCaseSensitive() {
return true;
}
}

View File

@ -0,0 +1,9 @@
package com.powertuple.intellij.haskell;
import com.intellij.lexer.FlexAdapter;
public class HaskellLexer extends FlexAdapter {
public HaskellLexer() {
super(new _HaskellLexer());
}
}

View File

@ -0,0 +1,9 @@
package com.powertuple.intellij.haskell;
import com.intellij.lexer.FlexAdapter;
public class HaskellLexerAdapter extends FlexAdapter {
public HaskellLexerAdapter() {
super(new _HaskellLexer());
}
}

View File

@ -0,0 +1,76 @@
package com.powertuple.intellij.haskell;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.lang.ParserDefinition;
import com.intellij.lang.PsiParser;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.project.Project;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.TokenSet;
import com.powertuple.intellij.haskell.parser.HaskellParser;
import com.powertuple.intellij.haskell.psi.HaskellFile;
import com.powertuple.intellij.haskell.psi.HaskellTypes;
import org.jetbrains.annotations.NotNull;
public class HaskellParserDefinition implements ParserDefinition {
public static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE);
public static final TokenSet COMMENTS = TokenSet.create(HaskellTypes.HS_COMMENT, HaskellTypes.HS_NCOMMENT);
@NotNull
@Override
public Lexer createLexer(Project project) {
return new HaskellLexerAdapter();
}
@Override
public PsiParser createParser(Project project) {
return new HaskellParser();
}
@Override
public IFileElementType getFileNodeType() {
return new IFileElementType(Language.findInstance(HaskellLanguage.class));
}
@NotNull
@Override
public TokenSet getWhitespaceTokens() {
return WHITE_SPACES;
}
@NotNull
@Override
public TokenSet getCommentTokens() {
return COMMENTS;
}
@NotNull
@Override
public TokenSet getStringLiteralElements() {
return TokenSet.create(
HaskellTypes.HS_CHARACTER_LITERAL,
HaskellTypes.HS_STRING_LITERAL
);
}
@NotNull
@Override
public PsiElement createElement(ASTNode node) {
return HaskellTypes.Factory.createElement(node);
}
@Override
public PsiFile createFile(FileViewProvider viewProvider) {
return new HaskellFile(viewProvider);
}
@Override
public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode left, ASTNode right) {
return SpaceRequirements.MAY;
}
}

View File

@ -0,0 +1,19 @@
package com.powertuple.intellij.haskell;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.parser.GeneratedParserUtilBase;
public class HaskellParserUtil extends GeneratedParserUtilBase {
public static boolean ghcMod(PsiBuilder builder_, int level_) {
// if (builder_.eof()) return true;
System.out.println("test rik van rikkie");
// IElementType one = builder_.rawLookup(1);
// IElementType two = builder_.rawLookup(2);
// if (one == TokenType.WHITE_SPACE && (two == HaskellTypes.HS_DOT || two == null) || one == null && builder_.getTokenType() == HaskellTypes.HS_DOT) {
// builder_.remapCurrentToken(TokenType.ERROR_ELEMENT);
// return true;
// }
return false;
}
}

View File

@ -0,0 +1,110 @@
package com.powertuple.intellij.haskell;
import com.intellij.lexer.*;
import com.intellij.psi.tree.IElementType;
import static com.powertuple.intellij.haskell.psi.HaskellTypes.*;
%%
%{
public _HaskellLexer() {
this((java.io.Reader)null);
}
%}
%public
%class _HaskellLexer
%implements FlexLexer
%function advance
%type IElementType
%unicode
ControlCharacter = [\000 - \037]
Whitespace = ([ \t\n] | {ControlCharacter})+
SMALL=[a-z_] // ignoring any unicode lowercase letter for now
LARGE=[A-Z] // ignoring any unicode uppercase letter for now
DIGIT=[0-9] // ignoring any unicode decimal digit for now
DECIMAL={DIGIT}+
HEXADECIMAL=0[xX][0-9A-Fa-f]+
OCTAL=0[oO][0-7]+
FLOAT=[-+]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][-+]?[0-9]+)?
COMMENT="--"[^\r\n]*
EOL=\r|\n|\r\n
NCOMMENT="{-" (.|{EOL})* "-}"
CHARACTER_LITERAL = \' [^\'\\\r\n]* \'
STRING_LITERAL = \" [^\"\\\r\n]* \"
%%
<YYINITIAL> {
{COMMENT} { return HS_COMMENT; }
{NCOMMENT} { return HS_NCOMMENT; }
{Whitespace} { return com.intellij.psi.TokenType.WHITE_SPACE; }
// "{" { return HS_LEFT_BRACE; }
// "}" { return HS_RIGHT_BRACE; }
// "[" { return HS_LEFT_BRACKET; }
// "]" { return HS_RIGHT_BRACKET; }
// "(" { return HS_LEFT_PAREN; }
// ")" { return HS_RIGHT_PAREN; }
// ":" { return HS_COLON;}
// ";" { return HS_SEMICOLON;}
// "." { return HS_DOT; }
// "," { return HS_COMMA; }
// "|" { return HS_VERTICAL_BAR;}
"=" { return HS_DEFINED_BY; }
"<-" { return HS_DRAW_FROM_OR_MATCHES_OR_IN; }
"=>" { return HS_INSTANCE_CONTEXTS; }
"<" { return HS_LT; }
">" { return HS_GT; }
// not listed as reserved identifier but have meaning in certain context
// "as" { return HS_AS; }
// "hiding" { return HS_HIDING; }
// "qualified" { return HS_QUALIFIED; }
// reserved identifiers
"case" { return HS_CASE_KEYWORD; }
"class" { return HS_CLASS_KEYWORD; }
"data" { return HS_DATA_KEYWORD; }
"default" { return HS_DEFAULT_KEYWORD; }
"deriving" { return HS_DERIVING_KEYWORD; }
// "do" { return HS_DO_KEYWORD; }
// "else" { return HS_ELSE_KEYWORD; }
// "hiding" { return HS_HIDING_KEYWORD; }
// "if" { return HS_IF_KEYWORD; }
// "import" { return HS_IMPORT_KEYWORD; }
// "in" { return HS_IN_KEYWORD; }
// "infix" { return HS_INFIX_KEYWORD; }
// "infixl" { return HS_INFIXL_KEYWORD; }
// "infixr" { return HS_INFIXR_KEYWORD; }
// "instance" { return HS_INSTANCE_KEYWORD; }
// "let" { return HS_LET_KEYWORD; }
"module" { return HS_MODULE_KEYWORD; }
// "newtype" { return HS_NEWTYPE_KEYWORD; }
// "of" { return HS_OF_KEYWORD; }
// "then" { return HS_THEN_KEYWORD; }
// "type" { return HS_TYPE_KEYWORD; }
"where" { return HS_WHERE_KEYWORD; }
// "_" { return HS___KEYWORD; }
{CHARACTER_LITERAL} { return HS_CHARACTER_LITERAL; }
{STRING_LITERAL} { return HS_STRING_LITERAL; }
{SMALL} { return HS_SMALL; }
{LARGE} { return HS_LARGE; }
{DECIMAL} { return HS_DECIMAL; }
{HEXADECIMAL} { return HS_HEXADECIMAL; }
{OCTAL} { return HS_OCTAL; }
{FLOAT} { return HS_FLOAT; }
[^] { return com.intellij.psi.TokenType.BAD_CHARACTER; }
}

View File

@ -0,0 +1,603 @@
/* The following code was generated by JFlex 1.4.3 on 5/12/14 10:37 AM */
package com.powertuple.intellij.haskell;
import com.intellij.lexer.*;
import com.intellij.psi.tree.IElementType;
import static com.powertuple.intellij.haskell.psi.HaskellTypes.*;
/**
* This class is a scanner generated by
* <a href="http://www.jflex.de/">JFlex</a> 1.4.3
* on 5/12/14 10:37 AM from the specification file
* <tt>/home/rik/idea/intellij-haskell/src/com/powertuple/intellij/haskell/_HaskellLexer.flex</tt>
*/
public class _HaskellLexer implements FlexLexer {
/** initial size of the lookahead buffer */
private static final int ZZ_BUFFERSIZE = 16384;
/** lexical states */
public static final int YYINITIAL = 0;
/**
* ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
* ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l
* at the beginning of a line
* l is of the form l = 2*k, k a non negative integer
*/
private static final int ZZ_LEXSTATE[] = {
0, 0
};
/**
* Translates characters to character classes
*/
private static final String ZZ_CMAP_PACKED =
"\1\1\10\0\1\1\1\17\2\0\1\23\21\0\1\1\1\1\1\0"+
"\1\27\4\0\1\26\3\0\1\15\1\0\1\22\2\0\1\5\7\14"+
"\2\4\2\0\1\31\1\30\1\32\2\0\4\11\1\21\1\11\10\3"+
"\1\13\10\3\1\7\2\3\1\0\1\16\2\0\1\2\1\0\1\34"+
"\1\10\1\33\1\37\1\20\1\41\1\47\1\52\1\44\2\2\1\36"+
"\1\50\1\46\1\12\2\2\1\43\1\35\1\40\1\42\1\45\1\51"+
"\1\6\2\2\1\24\1\0\1\25\uff82\0";
/**
* Translates characters to character classes
*/
private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED);
/**
* Translates DFA states to action switch labels.
*/
private static final int [] ZZ_ACTION = zzUnpackAction();
private static final String ZZ_ACTION_PACKED_0 =
"\1\0\1\1\1\2\1\3\1\4\2\5\6\1\1\6"+
"\1\7\1\10\4\3\4\0\1\11\2\0\1\12\2\0"+
"\1\13\1\0\1\14\1\15\1\16\6\0\2\11\1\0"+
"\1\17\1\20\10\0\1\21\1\22\3\0\1\23\2\0"+
"\1\24\3\0\1\25\2\0\1\26\1\27\1\0\1\30";
private static int [] zzUnpackAction() {
int [] result = new int[73];
int offset = 0;
offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
return result;
}
private static int zzUnpackAction(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
do result[j++] = value; while (--count > 0);
}
return j;
}
/**
* Translates a state to a row index in the transition table
*/
private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
private static final String ZZ_ROWMAP_PACKED_0 =
"\0\0\0\53\0\126\0\53\0\53\0\201\0\254\0\327"+
"\0\u0102\0\u012d\0\u0158\0\u0183\0\u01ae\0\u01d9\0\u0204\0\53"+
"\0\u022f\0\u025a\0\u0285\0\u02b0\0\u02db\0\u0306\0\u0331\0\u035c"+
"\0\u0387\0\u0102\0\u03b2\0\u03dd\0\u0408\0\u0183\0\53\0\u01ae"+
"\0\53\0\53\0\53\0\u0433\0\u045e\0\u0489\0\u04b4\0\u04df"+
"\0\u050a\0\u0535\0\u0560\0\u0560\0\u0331\0\u035c\0\u058b\0\u05b6"+
"\0\u05e1\0\u060c\0\u0637\0\u0662\0\u068d\0\u06b8\0\u0408\0\53"+
"\0\u06e3\0\u070e\0\u0739\0\53\0\u0764\0\u078f\0\53\0\u07ba"+
"\0\u07e5\0\u0810\0\53\0\u083b\0\u0866\0\53\0\53\0\u0891"+
"\0\53";
private static int [] zzUnpackRowMap() {
int [] result = new int[73];
int offset = 0;
offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
return result;
}
private static int zzUnpackRowMap(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int high = packed.charAt(i++) << 16;
result[j++] = high | packed.charAt(i++);
}
return j;
}
/**
* The transition table of the DFA
*/
private static final int [] ZZ_TRANS = zzUnpackTrans();
private static final String ZZ_TRANS_PACKED_0 =
"\1\2\1\3\1\4\1\5\1\6\1\7\1\4\1\5"+
"\1\4\1\5\1\4\1\5\1\6\1\10\1\11\1\3"+
"\1\4\1\5\1\12\1\2\1\13\1\2\1\14\1\15"+
"\1\16\1\17\1\20\1\21\3\4\1\22\10\4\1\23"+
"\1\24\1\4\54\0\1\3\15\0\1\3\37\0\2\6"+
"\6\0\1\6\1\0\1\25\1\0\2\26\35\0\2\6"+
"\2\27\2\0\2\30\1\6\1\0\1\25\1\0\2\26"+
"\35\0\2\31\6\0\1\31\1\0\1\32\34\0\17\33"+
"\1\0\33\33\4\0\2\31\6\0\1\31\1\0\1\32"+
"\3\0\1\34\52\0\1\35\30\0\16\36\2\0\3\36"+
"\1\0\2\36\1\37\24\36\16\40\2\0\3\40\1\0"+
"\3\40\1\41\23\40\32\0\1\42\42\0\1\43\64\0"+
"\1\44\1\0\1\45\34\0\1\46\13\0\1\47\30\0"+
"\1\50\112\0\1\51\17\52\1\0\33\52\4\0\2\53"+
"\6\0\1\53\1\54\4\0\1\54\34\0\2\55\2\0"+
"\2\55\2\0\1\55\3\0\2\55\11\0\2\55\2\0"+
"\1\55\1\0\1\55\16\0\1\56\6\0\1\56\42\0"+
"\2\31\6\0\1\31\1\0\1\25\1\0\2\26\35\0"+
"\2\52\6\0\1\52\36\0\17\34\1\0\3\34\1\0"+
"\27\34\22\35\1\57\30\35\35\0\1\60\51\0\1\61"+
"\57\0\1\62\1\0\1\63\47\0\1\64\51\0\1\65"+
"\33\0\1\66\36\0\2\52\6\0\1\52\3\0\2\26"+
"\35\0\2\53\6\0\1\53\36\0\22\35\1\57\2\35"+
"\1\67\25\35\20\0\1\70\67\0\1\71\51\0\1\72"+
"\62\0\1\73\42\0\1\74\60\0\1\75\53\0\1\76"+
"\44\0\1\77\57\0\1\100\55\0\1\101\43\0\1\102"+
"\34\0\1\103\70\0\1\104\60\0\1\105\26\0\1\106"+
"\72\0\1\107\60\0\1\110\53\0\1\111\3\0";
private static int [] zzUnpackTrans() {
int [] result = new int[2236];
int offset = 0;
offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
return result;
}
private static int zzUnpackTrans(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
value--;
do result[j++] = value; while (--count > 0);
}
return j;
}
/* error codes */
private static final int ZZ_UNKNOWN_ERROR = 0;
private static final int ZZ_NO_MATCH = 1;
private static final int ZZ_PUSHBACK_2BIG = 2;
private static final char[] EMPTY_BUFFER = new char[0];
private static final int YYEOF = -1;
private static java.io.Reader zzReader = null; // Fake
/* error messages for the codes above */
private static final String ZZ_ERROR_MSG[] = {
"Unkown internal scanner error",
"Error: could not match input",
"Error: pushback value was too large"
};
/**
* ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code>
*/
private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
private static final String ZZ_ATTRIBUTE_PACKED_0 =
"\1\0\1\11\1\1\2\11\12\1\1\11\4\1\4\0"+
"\1\1\2\0\1\1\2\0\1\11\1\0\3\11\6\0"+
"\2\1\1\0\2\1\10\0\1\1\1\11\3\0\1\11"+
"\2\0\1\11\3\0\1\11\2\0\2\11\1\0\1\11";
private static int [] zzUnpackAttribute() {
int [] result = new int[73];
int offset = 0;
offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
return result;
}
private static int zzUnpackAttribute(String packed, int offset, int [] result) {
int i = 0; /* index in packed string */
int j = offset; /* index in unpacked array */
int l = packed.length();
while (i < l) {
int count = packed.charAt(i++);
int value = packed.charAt(i++);
do result[j++] = value; while (--count > 0);
}
return j;
}
/** the current state of the DFA */
private int zzState;
/** the current lexical state */
private int zzLexicalState = YYINITIAL;
/** this buffer contains the current text to be matched and is
the source of the yytext() string */
private CharSequence zzBuffer = "";
/** this buffer may contains the current text array to be matched when it is cheap to acquire it */
private char[] zzBufferArray;
/** the textposition at the last accepting state */
private int zzMarkedPos;
/** the textposition at the last state to be included in yytext */
private int zzPushbackPos;
/** the current text position in the buffer */
private int zzCurrentPos;
/** startRead marks the beginning of the yytext() string in the buffer */
private int zzStartRead;
/** endRead marks the last character in the buffer, that has been read
from input */
private int zzEndRead;
/**
* zzAtBOL == true <=> the scanner is currently at the beginning of a line
*/
private boolean zzAtBOL = true;
/** zzAtEOF == true <=> the scanner is at the EOF */
private boolean zzAtEOF;
/* user code: */
public _HaskellLexer() {
this((java.io.Reader)null);
}
public _HaskellLexer(java.io.Reader in) {
this.zzReader = in;
}
/**
* Creates a new scanner.
* There is also java.io.Reader version of this constructor.
*
* @param in the java.io.Inputstream to read input from.
*/
public _HaskellLexer(java.io.InputStream in) {
this(new java.io.InputStreamReader(in));
}
/**
* Unpacks the compressed character translation table.
*
* @param packed the packed character translation table
* @return the unpacked character translation table
*/
private static char [] zzUnpackCMap(String packed) {
char [] map = new char[0x10000];
int i = 0; /* index in packed string */
int j = 0; /* index in unpacked array */
while (i < 132) {
int count = packed.charAt(i++);
char value = packed.charAt(i++);
do map[j++] = value; while (--count > 0);
}
return map;
}
public final int getTokenStart(){
return zzStartRead;
}
public final int getTokenEnd(){
return getTokenStart() + yylength();
}
public void reset(CharSequence buffer, int start, int end,int initialState){
zzBuffer = buffer;
zzBufferArray = com.intellij.util.text.CharArrayUtil.fromSequenceWithoutCopying(buffer);
zzCurrentPos = zzMarkedPos = zzStartRead = start;
zzPushbackPos = 0;
zzAtEOF = false;
zzAtBOL = true;
zzEndRead = end;
yybegin(initialState);
}
/**
* Refills the input buffer.
*
* @return <code>false</code>, iff there was new input.
*
* @exception java.io.IOException if any I/O-Error occurs
*/
private boolean zzRefill() throws java.io.IOException {
return true;
}
/**
* Returns the current lexical state.
*/
public final int yystate() {
return zzLexicalState;
}
/**
* Enters a new lexical state
*
* @param newState the new lexical state
*/
public final void yybegin(int newState) {
zzLexicalState = newState;
}
/**
* Returns the text matched by the current regular expression.
*/
public final CharSequence yytext() {
return zzBuffer.subSequence(zzStartRead, zzMarkedPos);
}
/**
* Returns the character at position <tt>pos</tt> from the
* matched text.
*
* It is equivalent to yytext().charAt(pos), but faster
*
* @param pos the position of the character to fetch.
* A value from 0 to yylength()-1.
*
* @return the character at position pos
*/
public final char yycharat(int pos) {
return zzBufferArray != null ? zzBufferArray[zzStartRead+pos]:zzBuffer.charAt(zzStartRead+pos);
}
/**
* Returns the length of the matched text region.
*/
public final int yylength() {
return zzMarkedPos-zzStartRead;
}
/**
* Reports an error that occured while scanning.
*
* In a wellformed scanner (no or only correct usage of
* yypushback(int) and a match-all fallback rule) this method
* will only be called with things that "Can't Possibly Happen".
* If this method is called, something is seriously wrong
* (e.g. a JFlex bug producing a faulty scanner etc.).
*
* Usual syntax/scanner level error handling should be done
* in error fallback rules.
*
* @param errorCode the code of the errormessage to display
*/
private void zzScanError(int errorCode) {
String message;
try {
message = ZZ_ERROR_MSG[errorCode];
}
catch (ArrayIndexOutOfBoundsException e) {
message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
}
throw new Error(message);
}
/**
* Pushes the specified amount of characters back into the input stream.
*
* They will be read again by then next call of the scanning method
*
* @param number the number of characters to be read again.
* This number must not be greater than yylength()!
*/
public void yypushback(int number) {
if ( number > yylength() )
zzScanError(ZZ_PUSHBACK_2BIG);
zzMarkedPos -= number;
}
/**
* Resumes scanning until the next regular expression is matched,
* the end of input is encountered or an I/O-Error occurs.
*
* @return the next token
* @exception java.io.IOException if any I/O-Error occurs
*/
public IElementType advance() throws java.io.IOException {
int zzInput;
int zzAction;
// cached fields:
int zzCurrentPosL;
int zzMarkedPosL;
int zzEndReadL = zzEndRead;
CharSequence zzBufferL = zzBuffer;
char[] zzBufferArrayL = zzBufferArray;
char [] zzCMapL = ZZ_CMAP;
int [] zzTransL = ZZ_TRANS;
int [] zzRowMapL = ZZ_ROWMAP;
int [] zzAttrL = ZZ_ATTRIBUTE;
while (true) {
zzMarkedPosL = zzMarkedPos;
zzAction = -1;
zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
zzState = ZZ_LEXSTATE[zzLexicalState];
zzForAction: {
while (true) {
if (zzCurrentPosL < zzEndReadL)
zzInput = (zzBufferArrayL != null ? zzBufferArrayL[zzCurrentPosL++] : zzBufferL.charAt(zzCurrentPosL++));
else if (zzAtEOF) {
zzInput = YYEOF;
break zzForAction;
}
else {
// store back cached positions
zzCurrentPos = zzCurrentPosL;
zzMarkedPos = zzMarkedPosL;
boolean eof = zzRefill();
// get translated positions and possibly new buffer
zzCurrentPosL = zzCurrentPos;
zzMarkedPosL = zzMarkedPos;
zzBufferL = zzBuffer;
zzEndReadL = zzEndRead;
if (eof) {
zzInput = YYEOF;
break zzForAction;
}
else {
zzInput = (zzBufferArrayL != null ? zzBufferArrayL[zzCurrentPosL++] : zzBufferL.charAt(zzCurrentPosL++));
}
}
int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ];
if (zzNext == -1) break zzForAction;
zzState = zzNext;
int zzAttributes = zzAttrL[zzState];
if ( (zzAttributes & 1) == 1 ) {
zzAction = zzState;
zzMarkedPosL = zzCurrentPosL;
if ( (zzAttributes & 8) == 8 ) break zzForAction;
}
}
}
// store back cached position
zzMarkedPos = zzMarkedPosL;
switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
case 22:
{ return HS_MODULE_KEYWORD;
}
case 25: break;
case 6:
{ return HS_DEFINED_BY;
}
case 26: break;
case 1:
{ return com.intellij.psi.TokenType.BAD_CHARACTER;
}
case 27: break;
case 3:
{ return HS_SMALL;
}
case 28: break;
case 14:
{ return HS_DRAW_FROM_OR_MATCHES_OR_IN;
}
case 29: break;
case 21:
{ return HS_WHERE_KEYWORD;
}
case 30: break;
case 19:
{ return HS_DATA_KEYWORD;
}
case 31: break;
case 16:
{ return HS_OCTAL;
}
case 32: break;
case 4:
{ return HS_LARGE;
}
case 33: break;
case 5:
{ return HS_DECIMAL;
}
case 34: break;
case 23:
{ return HS_DEFAULT_KEYWORD;
}
case 35: break;
case 13:
{ return HS_INSTANCE_CONTEXTS;
}
case 36: break;
case 18:
{ return HS_CASE_KEYWORD;
}
case 37: break;
case 15:
{ return HS_HEXADECIMAL;
}
case 38: break;
case 7:
{ return HS_LT;
}
case 39: break;
case 11:
{ return HS_CHARACTER_LITERAL;
}
case 40: break;
case 20:
{ return HS_CLASS_KEYWORD;
}
case 41: break;
case 9:
{ return HS_FLOAT;
}
case 42: break;
case 12:
{ return HS_STRING_LITERAL;
}
case 43: break;
case 10:
{ return HS_COMMENT;
}
case 44: break;
case 17:
{ return HS_NCOMMENT;
}
case 45: break;
case 2:
{ return com.intellij.psi.TokenType.WHITE_SPACE;
}
case 46: break;
case 24:
{ return HS_DERIVING_KEYWORD;
}
case 47: break;
case 8:
{ return HS_GT;
}
case 48: break;
default:
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
zzAtEOF = true;
return null;
}
else {
zzScanError(ZZ_NO_MATCH);
}
}
}
}
}

View File

@ -0,0 +1,129 @@
package com.powertuple.intellij.haskell.annotator
import com.intellij.lang.annotation.{AnnotationHolder, ExternalAnnotator}
import com.intellij.notification.NotificationGroup
import com.intellij.openapi.diagnostic.Logger
import com.intellij.psi.PsiFile
import com.powertuple.intellij.haskell.HaskellFileType
import com.powertuple.intellij.haskell.util.HaskellSystemUtil
import scala.collection.JavaConversions._
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.application.{ModalityState, ApplicationManager}
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.execution.process.ProcessOutput
import com.intellij.openapi.util.text.StringUtil
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
import com.intellij.openapi.ui.MessageType
class GhcModExternalAnnotator extends ExternalAnnotator[GhcModInitialInfo, GhcModResult] {
private val Log = Logger.getInstance(classOf[GhcModExternalAnnotator])
private val GhcModNotificationGroup = NotificationGroup.balloonGroup("Ghc-mod inspections")
/**
* Collects initial information required for ghc-mod.
*
* Returning null will cause doAnnotate() not to be called by Intellij API.
*/
override def collectInformation(psiFile: PsiFile): GhcModInitialInfo = {
val vFile = psiFile.getVirtualFile
vFile match {
case null => null // can be case if file is in memory only (just created file)
case f if f.getFileType != HaskellFileType.INSTANCE => null
case f if f.getCanonicalPath == null => null
case f => GhcModInitialInfo(psiFile, f.getCanonicalPath)
}
}
override def doAnnotate(initialInfoGhcMod: GhcModInitialInfo): GhcModResult = {
ApplicationManager.getApplication.invokeAndWait(new Runnable() {
override def run() {
FileDocumentManager.getInstance.saveDocument(FileDocumentManager.getInstance().getDocument(initialInfoGhcMod.psiFile.getVirtualFile))
}
}, ModalityState.any())
val ghcModOutput = HaskellSystemUtil.getProcessOutput(initialInfoGhcMod.psiFile.getProject.getBasePath, "/home/rik/.cabal/bin/ghc-mod", Seq("check", initialInfoGhcMod.filePath))
new GhcModResult(parseGhcModOutput(ghcModOutput))
}
override def apply(psiFile: PsiFile, ghcModResult: GhcModResult, holder: AnnotationHolder) {
if (!psiFile.isValid) {
return
}
if (ghcModResult.problems.isEmpty) {
markFileDirty(psiFile)
return
}
for (annotation <- createAnnotations(ghcModResult, psiFile.getText)) {
Log.info("annotation: " + annotation)
annotation match {
case ErrorAnnotation(textRange, message) => holder.createErrorAnnotation(textRange, message)
case WarningAnnotation(textRange, message) => holder.createWarningAnnotation(textRange, message)
}
}
markFileDirty(psiFile)
}
private def markFileDirty(psiFile: PsiFile) {
val fileStatusMap = DaemonCodeAnalyzer.getInstance(psiFile.getProject).asInstanceOf[DaemonCodeAnalyzerImpl].getFileStatusMap
val document = FileDocumentManager.getInstance().getDocument(psiFile.getVirtualFile)
fileStatusMap.markFileScopeDirty(document, new TextRange(0, document.getTextLength), psiFile.getTextLength)
}
private def startOffSetForProblem(lengthPerLine: Array[Int], problem: GhcModProblem): Int = {
val lineNr = problem.lineNr
(if (lineNr <= 1) {
0
} else {
lengthPerLine.take(lineNr - 1).sum
}) + problem.columnNr - 1
}
private[annotator] def createAnnotations(ghcModResult: GhcModResult, text: String): Seq[Annotation] = {
val lengthPerLine = StringUtil.splitByLines(text, false).map(_.size + 1)
for (problem <- ghcModResult.problems) yield {
Log.info("problem: " + problem)
val startOffSet = startOffSetForProblem(lengthPerLine, problem)
val textRange = TextRange.create(startOffSet, startOffSet + 1)
if (problem.description.startsWith("Warning:")) {
WarningAnnotation(textRange, problem.description)
} else {
ErrorAnnotation(textRange, problem.description)
}
}
}
private[annotator] def parseGhcModOutput(ghcModOutput: ProcessOutput): Seq[GhcModProblem] = {
ghcModOutput match {
case gmo if !gmo.getStderrLines.isEmpty => GhcModNotificationGroup.createNotification(s"Ghc-mod error output: ${gmo.getStderr}", MessageType.ERROR); Seq()
case gmo => gmo.getStdoutLines.map(parseGhcModOutputLine)
}
}
private def parseGhcModOutputLine(ghcModOutput: String): GhcModProblem = {
val ghcModProblemPattern = """.+:([\d]+):([\d]+):(.+)""".r
val ghcModProblemPattern(lineNr, columnNr, description) = ghcModOutput
new GhcModProblem(lineNr.toInt, columnNr.toInt, description)
}
}
case class GhcModInitialInfo(psiFile: PsiFile, filePath: String)
case class GhcModResult(problems: Seq[GhcModProblem] = Seq())
case class GhcModProblem(lineNr: Int, columnNr: Int, description: String)
abstract class Annotation
case class ErrorAnnotation(textRange: TextRange, message: String) extends Annotation
case class WarningAnnotation(textRange: TextRange, message: String) extends Annotation

View File

@ -0,0 +1,257 @@
// Derived from http://www.haskell.org/onlinereport/haskell2010/haskellch10.html
{
parserClass="com.powertuple.intellij.haskell.parser.HaskellParser"
extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
psiClassPrefix="Haskell"
psiImplClassSuffix="Impl"
psiPackage="com.powertuple.intellij.haskell.psi"
psiImplPackage="com.powertuple.intellij.haskell.psi.impl"
elementTypeHolderClass="com.powertuple.intellij.haskell.psi.HaskellTypes"
elementTypeClass="com.powertuple.intellij.haskell.psi.HaskellElementType"
tokenTypeClass="com.powertuple.intellij.haskell.psi.HaskellTokenType"
elementTypePrefix="HS_"
parserUtilClass="com.powertuple.intellij.haskell.HaskellParserUtil"
tokens = [
]
name(".*")='varide'
// extends(".*")=lexeme
}
program ::= (lexeme | COMMENT | NCOMMENT)*
private lexeme ::= qvarid | qconid | qvarsym | qconsym | literal | special | reservedop | reservedid
private literal ::= DECIMAL | HEXADECIMAL | OCTAL | FLOAT | CHARACTER_LITERAL | STRING_LITERAL
special ::= '(' | ')' | ',' | ';' | '[' | ']' | '`' | '{' | '}'
ascSymbolExceptBackslash ::= '!' | '#' | '$' | '%' | '&' | '*' | '+' | '.' | '/' | LT | '=' | GT | '?' | '@'
| '\' | '^' | '|' | '-' | '~' | ':'
ascSymbol ::= ascSymbolExceptBackslash | '\'
symbol ::= ascSymbol // ignoring non ascii
tyvar ::= varid
tycon ::= conid
tycls ::= conid
modid ::= (conid '.')* conid
qvarid ::= (modid '.')? varid
qconid ::= (modid '.')? conid
qtycon ::= (modid '.')? tycon
qtycls ::= (modid '.')? tycls
qvarsym ::= (modid '.')? varsym
qconsym ::= (modid '.')? consym
conid ::= LARGE (SMALL | LARGE)*
varid ::= SMALL (SMALL | LARGE)*
varsym ::= (symbol (symbol | ':')*)
consym ::= (':' (symbol | ':')*)
reservedid ::= CASE_KEYWORD | CLASS_KEYWORD | DATA_KEYWORD | DEFAULT_KEYWORD | DERIVING_KEYWORD | 'do' | 'else'
| 'if' | 'import' | 'in' | 'infix' | 'infixl' | 'infixr' | 'instance'
| 'let' | MODULE_KEYWORD | 'newtype' | 'of' | 'then' | 'type' | WHERE_KEYWORD | '_'
reservedop ::= '..' | ':' | '::' | DEFINED_BY | '\' | '|' | DRAW_FROM_OR_MATCHES_OR_IN | '->' | '@'
| '~' | INSTANCE_CONTEXTS
// Context-Free Syntax (under construction because left-recursion is not supported by generator)
//module ::= 'module' modid (exports)? 'where' body
// | body
//
//body ::= impdecls *
// | topdecls *
// | (impdecls ';' topdecls) *
//
//impdecls ::= impdecl(';' impdecl)*
//
//exports ::= '(' export(',' export)* ')'
//
//export ::= qvar
// | qtycon (('..') | ((cname(',' cname)* )?))?
// | qtycls (('..') | ((qvar (',' qvar)* )?))?
// | module modid
//
//impdecl ::= import ('qualified')? modid ('as' modid)? (impspec)?
//
//impspec ::= '(' (import (',' import )* (',' )?)? ')'
// | 'hiding' ((import (',' import )* (',' )?)?)
//import ::= var
// | tycon (('..') | ((cname (',' cname )*)?))?
//cname ::= var | con
//topdecls ::= (topdecl (';' topdecl)*)?
//topdecl ::= 'type' simpletype DEFINED_BY type
// | 'data' ( context '=>' )? simpletype DEFINED_BY constrs ( deriving )?
// | 'newtype' ( context '=>' )? simpletype DEFINED_BY newconstr ( deriving )?
// | 'class' ( scontext '=>' )? tycls tyvar ('where' cdecls)?
// | 'instance' ( scontext '=>' )? qtycls inst ('where' idecls)?
// | 'default' (( type (',' type)* )? )
// | 'foreign' fdecl
// | decl
//decls ::= '{' (decl (';' decl )*)? '}'
//decl ::= gendecl
// | (funlhs | pat) rhs
//cdecls ::= '{' (cdecl (';' cdecl )*)? '}'
//cdecl ::= gendecl
// | (funlhs | var) rhs
//idecls ::= '{' (idecl ( ';' idecl)* )? '}'
//idecl ::= (funlhs | var ) rhs
//gendecl ::= vars '::' (context '=>')? type // type signature
// | fixity (integer)? ops // fixity signature
// | // empty declaration
//
//ops ::= op (',' op )*
//vars ::= var (',' var)*
//fixity ::= 'infixl' | 'infixr' | 'infix'
//
//type ::= btype ('->' type)? // function type
//
//btype ::= (btype)? atype // type application
//
//atype ::= gtycon
// | tyvar
// | (type(',' type )+) // tuple type
// | '['type']' // list type
// | '('type')' // parenthesized constructor
//
//gtycon ::= qtycon
// | '()' // unit type
// | '[]' // list constructor
// | '(->)' // function constructor
// | '(,' (',')* ')' // tupling constructor
//
//context ::= class
// | '(' (class (',' class)* )? ')'
//class ::= qtycls tyvar
// | qtycls (tyvar (atype)+ )
//scontext ::= simpleclass
// | '(' (simpleclass (',' simpleclass)* )? ')'
//simpleclass ::= qtycls tyvar
//
//simpletype ::= tycon (tyvar)*
//constrs ::= constr (| constr)*
//constr ::= con (('!')? atype)*
// | (btype | '!' atype) conop (btype | '!' atype)
// | con '{' (fielddecl (',' fielddecl)* )? '}'
//newconstr ::= con atype
// | con '{' var '::' type '}'
//fielddecl ::= vars '::' (type | '!' atype)
//deriving ::= 'deriving' (dclass | '(' (dclass (',' dclass)* )? ')' )
//dclass ::= qtycls
//
//inst ::= gtycon
// | '(' gtycon ( tyvar )* ')'
// | '(' tyvar ( ',' tyvar )+ ')'
// | '(' tyvar '->' tyvar ')'
// | (tyvar)?
//
//fdecl ::= 'import' callconv (safety)? impent var '::' ftype
// | 'export' callconv expent var '::' ftype
//callconv ::= 'ccall' | 'stdcall' | 'cplusplus' // (calling convention)
// | 'jvm' | 'dotnet' // | 'system-specific calling conventions'????
//impent ::= (string_)?
//expent ::= (string_)?
//safety ::= 'unsafe' | 'safe'
//
//ftype ::= frtype
// | fatype ::= ftype // do not know how to handle this
//
//frtype ::= fatype
// | '()'
//fatype ::= qtycon (atype)*
//
//funlhs ::= var (apat)+
// | pat varop pat
// | '(' funlhs ')' (apat)+
//rhs ::= DEFINED_BY exp ('where' decls)?
// | gdrhs ('where' decls)?
//gdrhs ::= guards DEFINED_BY exp (gdrhs)?
//guards ::= '|' guard+
//guard ::= pat '<-' infixexp
// | 'let' decls
// | infixexp
//
//exp ::= infixexp '::' (context '=>')? type
// | infixexp
//
//infixexp ::= lexp qop infixexp
// | '-' infixexp
// | lexp
//
//lexp ::= '\' (apat)+ '->' exp
// | 'let' decls 'in' exp
// | 'if' exp 'then' exp 'else' exp
// | 'case' exp 'of' '{' alts '}'
// | 'do' '{' stmts '}'
// | fexp
//
//fexp ::= (fexp)? aexp
//
//aexp ::= qvar
// | gcon
// | literal
// | '(' exp ')'
// | '(' exp (',' exp )+ ')'
// | '[' exp (',' exp )* ']'
// | '[' exp (',' exp )? '..' ( exp )? ']'
// | '[' exp | qual (',' qual )* ']'
// | '(' infixexp qop ')'
// | '(' qop infixexp ')'
// | qcon '{' (fbind ( ',' fbind)* )? '}'
// | aexp '{' fbind (',' fbind)* '}'
//
//qual ::= pat '<-' exp
// | 'let' decls
// | exp
//
//alts ::= (alt (';' alt )* )?
//alt ::= pat '->' exp ('where' decls)?
// | pat gdpat ('where' decls)?
// | // (empty alternative)
//gdpat ::= guards '->' exp (gdpat)?
//stmts ::= (stmt)* exp (';')?
//stmt ::= exp ';'
// | pat '<-' exp ';'
// | 'let' decls ';'
// | ';' // (empty statement)
//fbind ::= qvar DEFINED_BY exp
//pat ::= lpat qconop pat // (infix constructor)
// | lpat
//
//left lpat ::= apat
// | '-' (integer | float) // (negative literal)
// | gcon (apat)+
//
//apat ::= var ('@' apat)? // (as pattern)
// | gcon // (arity gcon = 0)
// | qcon (fpat)* // (labeled pattern, k ≥ 0)
// | literal
// | '_' // (wildcard)
// | '('pat')' // (parenthesized pattern)
// | '('pat (',' pat)+ ')' // (tuple pattern, k ≥ 2)
// | '['pat (',' pat)* ']' // (list pattern, k ≥ 1)
// | '~' apat
//
//fpat ::= qvar DEFINED_BY pat
//
//gcon ::= '()'
// | '[]'
// | '(' (',')* ')'
// | qcon
//
//var ::= varid | '(' varsym ')' // variable
//qvar ::= qvarid | '(' qvarsym ')'
//con ::= conid | '(' consym ')'
//qcon ::= qconid | '(' gconsym ')'
//varop ::= varsym | '`' varid '`'
//qvarop ::= qvarsym | '`' qvarid '`'
//conop ::= consym | '`' conid '`'
//qconop ::= gconsym | '`' qconid '`'
//op ::= varop | conop
//qop ::= qvarop | qconop
//gconsym ::= ':' | qconsym

View File

@ -0,0 +1,57 @@
package com.powertuple.intellij.haskell.highlighter;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.powertuple.intellij.haskell.HaskellLexer;
import com.powertuple.intellij.haskell.psi.HaskellTypes;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
public class HaskellSyntaxHighlighter extends SyntaxHighlighterBase {
private static final Map<IElementType, TextAttributesKey> ATTRIBUTES = new HashMap<IElementType, TextAttributesKey>();
static final TokenSet LINE_COMMENTS = TokenSet.create(
HaskellTypes.HS_COMMENT
);
static final TokenSet NESTED_COMMENTS = TokenSet.create(
HaskellTypes.HS_NCOMMENT
);
static final TokenSet KEYWORDS = TokenSet.create(
HaskellTypes.HS_CASE_KEYWORD,
HaskellTypes.HS_MODULE_KEYWORD,
HaskellTypes.HS_WHERE_KEYWORD
);
static final TokenSet OPERATORS = TokenSet.create(
HaskellTypes.HS_DEFINED_BY,
HaskellTypes.HS_DRAW_FROM_OR_MATCHES_OR_IN
);
static {
SyntaxHighlighterBase.fillMap(ATTRIBUTES, KEYWORDS, DefaultLanguageHighlighterColors.KEYWORD);
SyntaxHighlighterBase.fillMap(ATTRIBUTES, LINE_COMMENTS, DefaultLanguageHighlighterColors.LINE_COMMENT);
SyntaxHighlighterBase.fillMap(ATTRIBUTES, NESTED_COMMENTS, DefaultLanguageHighlighterColors.BLOCK_COMMENT);
SyntaxHighlighterBase.fillMap(ATTRIBUTES, OPERATORS, DefaultLanguageHighlighterColors.OPERATION_SIGN);
}
@NotNull
@Override
public Lexer getHighlightingLexer() {
return new HaskellLexer();
}
@NotNull
@Override
public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {
return pack(ATTRIBUTES.get(tokenType));
}
}

View File

@ -0,0 +1,16 @@
package com.powertuple.intellij.haskell.highlighter;
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class HaskellSyntaxHighlighterFactory extends SyntaxHighlighterFactory {
@NotNull
@Override
public SyntaxHighlighter getSyntaxHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile) {
return new HaskellSyntaxHighlighter();
}
}

View File

@ -0,0 +1,12 @@
package com.powertuple.intellij.haskell.psi;
import com.intellij.psi.tree.IElementType;
import com.powertuple.intellij.haskell.HaskellLanguage;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class HaskellElementType extends IElementType {
public HaskellElementType(@NotNull @NonNls String debugName) {
super(debugName, HaskellLanguage.INSTANCE);
}
}

View File

@ -0,0 +1,32 @@
package com.powertuple.intellij.haskell.psi;
import com.intellij.extapi.psi.PsiFileBase;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.psi.FileViewProvider;
import com.powertuple.intellij.haskell.HaskellFileType;
import com.powertuple.intellij.haskell.HaskellLanguage;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public class HaskellFile extends PsiFileBase {
public HaskellFile(@NotNull FileViewProvider viewProvider) {
super(viewProvider, HaskellLanguage.INSTANCE);
}
@NotNull
@Override
public FileType getFileType() {
return HaskellFileType.INSTANCE;
}
@Override
public String toString() {
return "Haskell File";
}
@Override
public Icon getIcon(int flags) {
return super.getIcon(flags);
}
}

View File

@ -0,0 +1,17 @@
package com.powertuple.intellij.haskell.psi;
import com.intellij.psi.tree.IElementType;
import com.powertuple.intellij.haskell.HaskellLanguage;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class HaskellTokenType extends IElementType {
public HaskellTokenType(@NotNull @NonNls String debugName) {
super(debugName, HaskellLanguage.INSTANCE);
}
@Override
public String toString() {
return "HaskellTokenType." + super.toString();
}
}

View File

@ -0,0 +1,4 @@
package com.powertuple.intellij.haskell.psi.impl;
public class HaskellPsiImplUtil {
}

View File

@ -0,0 +1,31 @@
package com.powertuple.intellij.haskell.util
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.process.CapturingProcessHandler
import com.intellij.execution.process.ProcessOutput
import java.io.File
import scala.collection.JavaConversions._
object HaskellSystemUtil {
val StandardTimeout = 10 * 1000
def getProcessOutput(workDir: String, exePath: String, arguments: Seq[String], timeout: Int = StandardTimeout): ProcessOutput = {
if (!new File(workDir).isDirectory || !new File(exePath).canExecute) {
new ProcessOutput
}
val cmd: GeneralCommandLine = new GeneralCommandLine
cmd.setWorkDirectory(workDir)
cmd.setExePath(exePath)
cmd.addParameters(arguments)
execute(cmd, timeout)
}
def execute(cmd: GeneralCommandLine): ProcessOutput = {
execute(cmd, StandardTimeout)
}
def execute(cmd: GeneralCommandLine, timeout: Int): ProcessOutput = {
val processHandler: CapturingProcessHandler = new CapturingProcessHandler(cmd.createProcess)
if (timeout < 0) processHandler.runProcess else processHandler.runProcess(timeout)
}
}

View File

@ -0,0 +1,72 @@
package com.powertuple.intellij.haskell.annotator
import org.scalatest.{BeforeAndAfterEach, GivenWhenThen, Matchers, FunSpec}
import com.intellij.execution.process.ProcessOutput
class GhcModExternalAnnotatorSpec extends FunSpec with Matchers with GivenWhenThen with BeforeAndAfterEach {
val ghcModExternalAnnotator = new GhcModExternalAnnotator
describe("Parsing ghc-mod output") {
Given("output of ghc-mod")
val output = new ProcessOutput()
output.appendStdout("file/path/HaskellFile.hs:1:11:parse error on input\n")
output.appendStdout("file/path/HaskellFile.hs:12:5:another parse error on input")
When("parsed to problem list")
val problems = ghcModExternalAnnotator.parseGhcModOutput(output)
Then("list should have size 2")
problems should have size 2
val problem1 = problems(0)
val problem2 = problems(1)
And("contain right data")
problem1.lineNr should equal(1)
problem1.columnNr should equal(11)
problem1.description should equal("parse error on input")
problem2.lineNr should equal(12)
problem2.columnNr should equal(5)
problem2.description should equal("another parse error on input")
}
describe("Determine annotation offset when compile error") {
Given("some Haskell code which gives compile errors")
val someCode =
"""
|Some Haskell code
|which does not
|
|com pile
""".stripMargin
When("ghc-mod is executed")
val ghcModResult = GhcModResult(Seq(GhcModProblem(4, 3, "something wrong")))
val annotations = ghcModExternalAnnotator.createAnnotations(ghcModResult, someCode)
Then("annotation holder should contain right annotation")
annotations should have length 1
val annotation = annotations(0)
annotation.asInstanceOf[ErrorAnnotation].textRange.getStartOffset should equal(36)
}
describe("Determine annotation offset when compile warning") {
Given("some Haskell code which gives compile warnings")
val someCode =
"""
|some code
""".stripMargin
When("ghc-mod is executed")
val ghcModResult = GhcModResult(Seq(GhcModProblem(1, 1, "Warning: some warning")))
val annotations = ghcModExternalAnnotator.createAnnotations(ghcModResult, someCode)
Then("annotation holder should contain right annotation")
annotations should have length 1
val annotation = annotations(0)
annotation.asInstanceOf[WarningAnnotation].textRange.getStartOffset should equal(0)
}
}

View File

@ -0,0 +1,231 @@
package com.powertuple.intellij.haskell.annotator;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.application.ex.PathManagerEx;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.ModuleTestCase;
import com.intellij.testFramework.PsiTestData;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.util.IncorrectOperationException;
import com.powertuple.intellij.haskell.HaskellFileType;
import org.jdom.Document;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.StringTokenizer;
/**
* @author Mike
*/
public abstract class PsiTestCase extends ModuleTestCase {
protected PsiManagerImpl myPsiManager;
protected PsiFile myFile;
protected PsiTestData myTestDataBefore;
protected PsiTestData myTestDataAfter;
private String myDataRoot;
@Override
protected void setUp() throws Exception {
super.setUp();
myPsiManager = (PsiManagerImpl) PsiManager.getInstance(myProject);
}
@Override
protected void tearDown() throws Exception {
myPsiManager = null;
myFile = null;
myTestDataBefore = null;
myTestDataAfter = null;
super.tearDown();
}
protected PsiFile createDummyFile(String fileName, String text) throws IncorrectOperationException {
return PsiFileFactory.getInstance(myProject).createFileFromText(fileName, HaskellFileType.INSTANCE, text);
}
protected PsiFile createFile(@NonNls String fileName, String text) throws Exception {
return createFile(myModule, fileName, text);
}
protected PsiFile createFile(Module module, String fileName, String text) throws Exception {
File dir = createTempDirectory();
VirtualFile vDir = LocalFileSystem.getInstance().refreshAndFindFileByPath(dir.getCanonicalPath().replace(File.separatorChar, '/'));
return createFile(module, vDir, fileName, text);
}
protected PsiFile createFile(final Module module, final VirtualFile vDir, final String fileName, final String text) throws IOException {
return new WriteAction<PsiFile>() {
@Override
protected void run(Result<PsiFile> result) throws Throwable {
if (!ModuleRootManager.getInstance(module).getFileIndex().isInSourceContent(vDir)) {
addSourceContentToRoots(module, vDir);
}
final VirtualFile vFile = vDir.createChildData(vDir, fileName);
VfsUtil.saveText(vFile, text);
assertNotNull(vFile);
final PsiFile file = myPsiManager.findFile(vFile);
assertNotNull(file);
result.setResult(file);
}
}.execute().getResultObject();
}
protected void addSourceContentToRoots(final Module module, final VirtualFile vDir) {
PsiTestUtil.addSourceContentToRoots(module, vDir);
}
protected PsiElement configureByFileWithMarker(String filePath, String marker) throws Exception{
final VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(filePath.replace(File.separatorChar, '/'));
assertNotNull("file " + filePath + " not found", vFile);
String fileText = VfsUtil.loadText(vFile);
fileText = StringUtil.convertLineSeparators(fileText);
int offset = fileText.indexOf(marker);
assertTrue(offset >= 0);
fileText = fileText.substring(0, offset) + fileText.substring(offset + marker.length());
myFile = createFile(vFile.getName(), fileText);
return myFile.findElementAt(offset);
}
protected void configure(@NotNull String path, String dataName) throws Exception {
myDataRoot = getTestDataPath() + path;
myTestDataBefore = loadData(dataName);
PsiTestUtil.removeAllRoots(myModule, IdeaTestUtil.getMockJdk17());
VirtualFile vDir = PsiTestUtil.createTestProjectStructure(myProject, myModule, myDataRoot, myFilesToDelete);
final VirtualFile vFile = vDir.findChild(myTestDataBefore.getTextFile());
myFile = myPsiManager.findFile(vFile);
}
protected String getTestDataPath() {
return PathManagerEx.getTestDataPath();
}
protected String loadFile(String name) throws Exception {
String result = FileUtil.loadFile(new File(getTestDataPath() + File.separatorChar + name));
return StringUtil.convertLineSeparators(result);
}
private PsiTestData loadData(String dataName) throws Exception {
Document document = JDOMUtil.loadDocument(new File(myDataRoot + "/" + "data.xml"));
PsiTestData data = createData();
Element documentElement = document.getRootElement();
final List nodes = documentElement.getChildren("data");
for (Object node1 : nodes) {
Element node = (Element)node1;
String value = node.getAttributeValue("name");
if (value.equals(dataName)) {
DefaultJDOMExternalizer.readExternal(data, node);
data.loadText(myDataRoot);
return data;
}
}
throw new IllegalArgumentException("Cannot find data chunk '" + dataName + "'");
}
protected PsiTestData createData() {
return new PsiTestData();
}
protected void checkResult(String dataName) throws Exception {
myTestDataAfter = loadData(dataName);
final String textExpected = myTestDataAfter.getText();
final String actualText = myFile.getText();
if (!textExpected.equals(actualText)) {
System.out.println("Text mismatch: " + getName() + "(" + getClass().getName() + ")");
System.out.println("Text expected:");
printText(textExpected);
System.out.println("Text found:");
printText(actualText);
fail("text");
}
// assertEquals(myTestDataAfter.getText(), myFile.getText());
}
protected static void printText(String text) {
final String q = "\"";
System.out.print(q);
text = StringUtil.convertLineSeparators(text);
StringTokenizer tokenizer = new StringTokenizer(text, "\n", true);
while (tokenizer.hasMoreTokens()) {
final String token = tokenizer.nextToken();
if (token.equals("\n")) {
System.out.print(q);
System.out.println();
System.out.print(q);
continue;
}
System.out.print(token);
}
System.out.print(q);
System.out.println();
}
protected void addLibraryToRoots(final VirtualFile jarFile, OrderRootType rootType) {
addLibraryToRoots(myModule, jarFile, rootType);
}
protected static void addLibraryToRoots(final Module module, final VirtualFile root, final OrderRootType rootType) {
assertEquals(OrderRootType.CLASSES, rootType);
ModuleRootModificationUtil.addModuleLibrary(module, root.getUrl());
}
public PsiFile getFile() {
return myFile;
}
public com.intellij.openapi.editor.Document getDocument(PsiFile file) {
return PsiDocumentManager.getInstance(getProject()).getDocument(file);
}
public com.intellij.openapi.editor.Document getDocument(VirtualFile file) {
return FileDocumentManager.getInstance().getDocument(file);
}
public void commitDocument(com.intellij.openapi.editor.Document document) {
PsiDocumentManager.getInstance(getProject()).commitDocument(document);
}
}