├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── JFlex.jar
├── LICENCE.txt
├── META-INF
└── plugin.xml
├── README.md
├── idea-flex.skeleton
├── pycharm-pyxl.iml
├── pycharm-pyxl.jar
├── src
└── com
│ └── christofferklang
│ └── pyxl
│ ├── PyxlColorSettingsPage.java
│ ├── PyxlDialectTokenContributor.java
│ ├── PyxlElementTypes.java
│ ├── PyxlFormattingModelBuilder.java
│ ├── PyxlHighlighter.java
│ ├── PyxlHighlighterColors.java
│ ├── PyxlHighlighterFactory.java
│ ├── PyxlModuleReference.java
│ ├── PyxlTokenTypes.java
│ ├── parsing
│ ├── Pyxl.flex
│ ├── PyxlExpressionParsing.java
│ ├── PyxlHighlightingLexer.java
│ ├── PyxlIndentingLexer.java
│ ├── PyxlLexer.java
│ ├── PyxlParser.java
│ ├── PyxlParserDefinition.java
│ └── PyxlParsingContext.java
│ └── psi
│ ├── PythonClassReference.java
│ ├── PyxlArgumentList.java
│ ├── PyxlAttrName.java
│ └── PyxlTag.java
├── testdata
├── Attributes.py
├── Attributes.txt
├── Comments.py
├── Comments.txt
├── ParsingTestData.py
├── ParsingTestData.txt
├── TagNames.py
├── TagNames.txt
├── WithStatements.py
├── WithStatements.txt
├── class_self_ref.py
├── class_self_ref.txt
├── nestedembed.py
└── nestedembed.txt
└── tests
└── PyxlParsingTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | out/
2 |
3 | # This is recommended idea ignores (http://stackoverflow.com/questions/11968531)
4 | **/.idea/workspace.xml
5 | **/.idea/tasks.xml
6 |
7 | .DS_Store
8 | src/com/christofferklang/pyxl/parsing/_PyxlLexer.java
9 | *.java~
10 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | pycharm-pyxl
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Class structureJava
15 |
16 |
17 | Code maturity issuesJava
18 |
19 |
20 | Java
21 |
22 |
23 | Java language level migration aidsJava
24 |
25 |
26 | Javadoc issuesJava
27 |
28 |
29 | Performance issuesJava
30 |
31 |
32 | TestNGJava
33 |
34 |
35 | Threading issuesJava
36 |
37 |
38 |
39 |
40 | Android
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/JFlex.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christoffer/pycharm-pyxl/2c7cbe56196307923aa0c4eb8fd54aa067e16d85/JFlex.jar
--------------------------------------------------------------------------------
/LICENCE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Christoffer Klang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | com.christofferklang.pyxl
3 | Pyxl Support
4 | 1.2
5 | Christoffer Klang
6 |
7 | Pyxl files.
9 |
10 | Created by Nils Bunger (nils@dropbox.com), Robert Kajic (kajic@dropbox.com) and Christoffer Klang (christoffer@dropbox.com).
11 |
12 | Please report any bugs or missing features to https://github.com/christoffer/pycharm-pyxl, or email any of the creators.
13 |
14 | Requires PyCharm or the Python plugin.
15 | ]]>
16 |
17 |
19 | IDEA 2016.1 support
20 |
21 | ]]>
22 |
23 |
24 |
25 |
26 |
27 |
29 |
32 |
33 |
34 |
39 |
40 |
45 |
46 |
47 |
48 |
49 |
50 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | com.intellij.modules.python
63 |
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Pyxl Extensions for PyCharm
2 | ===========================
3 |
4 | This PyCharm plugin aims to provide extensions to PyCharm for working with [Pyxl](https://github.com/dropbox/pyxl) files.
5 |
6 | Written by [Nils Bunger](https://github.com/nilsbunger), [Robert Kajic](https://github.com/kajic) and [Christoffer Klang](https://github.com/christoffer).
7 |
8 | Installation
9 | ============
10 |
11 | Download the [plugin jar](/pycharm-pyxl.jar?raw=true) and switch over to `PyCharm > Settings > Plugins > Install plugin from disk...` and select the Jar.
12 |
13 | ## Release 1.1
14 | - Performance fixes
15 | - Add proper support for language level Python syntax (`with as:`, etc).
16 | - Fix xml namespaced attributes
17 | - Add support for `` and ``
18 |
19 | ## Release 1.2
20 | - PyCharm 2016.1 support
21 |
22 | Development
23 | ===========
24 |
25 | ### 1. Set up plugin development environment
26 | Follow [helful guide from Jetbrains](http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/setting_up_environment.html)
27 |
28 | ### 2. Git clone this repo.
29 |
30 | ### 3. Generate lexer file using provided JFlex (provided in repo root):
31 | ```
32 | $ java -jar JFlex.jar --skel idea-flex.skeleton --charat src/com/christofferklang/pyxl/parsing/Pyxl.flex
33 | ```
34 |
35 | ### 4. Add Python plugin dependency
36 | First install the Python plugin in IntelliJ (`Settings > Plugins`). Then locate `python.jar` in your home directory. The exact location will vary depending on OS and IntelliJ version. For Idea 2016.2 on Linux for example, the file can be found in `$HOME/.IntelliJIdea2016.2/config/plugins/python/lib`. On OSX it would be in ``$HOME/Library/Application Support/IntelliJIdea2016.2/python/lib`. You'll probably figure it out by running `find . -name 'python.jar' | grep python/lib` in your home directory.
37 |
38 | Once located, you need to add the jar file as a dependency to the project.
39 | `File > Project Structure... > Modules > pycharm-pyxl > Dependencies`
40 | (Click the green + to add a new dependency)
41 | `> `JARs or directories` (locate python.jar).
42 |
43 | Make sure the Python JAR is above ``, and that the scope is set to "Provided".
44 |
45 | ### 5. Open this project in IDEA
46 | Then right-click on pycharm-pyxl project and choose "Prepare plugin module for development"
47 | at the bottom of context menu. This will generate new jar in the project root which you can add to your PyCharm.
48 |
49 | ### 6. Pro-tip - you can launch PyCharm in debug mode to actually debug the plugin.
50 |
--------------------------------------------------------------------------------
/idea-flex.skeleton:
--------------------------------------------------------------------------------
1 | /** initial size of the lookahead buffer */
2 | --- private static final int ZZ_BUFFERSIZE = ...;
3 |
4 | /** lexical states */
5 | --- lexical states, charmap
6 |
7 | /* error codes */
8 | private static final int ZZ_UNKNOWN_ERROR = 0;
9 | private static final int ZZ_NO_MATCH = 1;
10 | private static final int ZZ_PUSHBACK_2BIG = 2;
11 | private static final char[] EMPTY_BUFFER = new char[0];
12 | private static final int YYEOF = -1;
13 | private static java.io.Reader zzReader = null; // Fake
14 |
15 | /* error messages for the codes above */
16 | private static final String ZZ_ERROR_MSG[] = {
17 | "Unkown internal scanner error",
18 | "Error: could not match input",
19 | "Error: pushback value was too large"
20 | };
21 |
22 | --- isFinal list
23 | /** the current state of the DFA */
24 | private int zzState;
25 |
26 | /** the current lexical state */
27 | private int zzLexicalState = YYINITIAL;
28 |
29 | /** this buffer contains the current text to be matched and is
30 | the source of the yytext() string */
31 | private CharSequence zzBuffer = "";
32 |
33 | /** this buffer may contains the current text array to be matched when it is cheap to acquire it */
34 | private char[] zzBufferArray;
35 |
36 | /** the textposition at the last accepting state */
37 | private int zzMarkedPos;
38 |
39 | /** the textposition at the last state to be included in yytext */
40 | private int zzPushbackPos;
41 |
42 | /** the current text position in the buffer */
43 | private int zzCurrentPos;
44 |
45 | /** startRead marks the beginning of the yytext() string in the buffer */
46 | private int zzStartRead;
47 |
48 | /** endRead marks the last character in the buffer, that has been read
49 | from input */
50 | private int zzEndRead;
51 |
52 | /**
53 | * zzAtBOL == true <=> the scanner is currently at the beginning of a line
54 | */
55 | private boolean zzAtBOL = true;
56 |
57 | /** zzAtEOF == true <=> the scanner is at the EOF */
58 | private boolean zzAtEOF;
59 |
60 | --- user class code
61 |
62 | --- constructor declaration
63 |
64 | public final int getTokenStart(){
65 | return zzStartRead;
66 | }
67 |
68 | public final int getTokenEnd(){
69 | return getTokenStart() + yylength();
70 | }
71 |
72 | public void reset(CharSequence buffer, int start, int end,int initialState){
73 | zzBuffer = buffer;
74 | zzBufferArray = com.intellij.util.text.CharArrayUtil.fromSequenceWithoutCopying(buffer);
75 | zzCurrentPos = zzMarkedPos = zzStartRead = start;
76 | zzPushbackPos = 0;
77 | zzAtEOF = false;
78 | zzAtBOL = true;
79 | zzEndRead = end;
80 | yybegin(initialState);
81 | }
82 |
83 | /**
84 | * Refills the input buffer.
85 | *
86 | * @return false
, iff there was new input.
87 | *
88 | * @exception java.io.IOException if any I/O-Error occurs
89 | */
90 | private boolean zzRefill() throws java.io.IOException {
91 | return true;
92 | }
93 |
94 |
95 | /**
96 | * Returns the current lexical state.
97 | */
98 | public final int yystate() {
99 | return zzLexicalState;
100 | }
101 |
102 |
103 | /**
104 | * Enters a new lexical state
105 | *
106 | * @param newState the new lexical state
107 | */
108 | public final void yybegin(int newState) {
109 | zzLexicalState = newState;
110 | }
111 |
112 |
113 | /**
114 | * Returns the text matched by the current regular expression.
115 | */
116 | public final CharSequence yytext() {
117 | return zzBuffer.subSequence(zzStartRead, zzMarkedPos);
118 | }
119 |
120 |
121 | /**
122 | * Returns the character at position pos from the
123 | * matched text.
124 | *
125 | * It is equivalent to yytext().charAt(pos), but faster
126 | *
127 | * @param pos the position of the character to fetch.
128 | * A value from 0 to yylength()-1.
129 | *
130 | * @return the character at position pos
131 | */
132 | public final char yycharat(int pos) {
133 | return zzBufferArray != null ? zzBufferArray[zzStartRead+pos]:zzBuffer.charAt(zzStartRead+pos);
134 | }
135 |
136 |
137 | /**
138 | * Returns the length of the matched text region.
139 | */
140 | public final int yylength() {
141 | return zzMarkedPos-zzStartRead;
142 | }
143 |
144 |
145 | /**
146 | * Reports an error that occured while scanning.
147 | *
148 | * In a wellformed scanner (no or only correct usage of
149 | * yypushback(int) and a match-all fallback rule) this method
150 | * will only be called with things that "Can't Possibly Happen".
151 | * If this method is called, something is seriously wrong
152 | * (e.g. a JFlex bug producing a faulty scanner etc.).
153 | *
154 | * Usual syntax/scanner level error handling should be done
155 | * in error fallback rules.
156 | *
157 | * @param errorCode the code of the errormessage to display
158 | */
159 | --- zzScanError declaration
160 | String message;
161 | try {
162 | message = ZZ_ERROR_MSG[errorCode];
163 | }
164 | catch (ArrayIndexOutOfBoundsException e) {
165 | message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
166 | }
167 |
168 | --- throws clause
169 | }
170 |
171 |
172 | /**
173 | * Pushes the specified amount of characters back into the input stream.
174 | *
175 | * They will be read again by then next call of the scanning method
176 | *
177 | * @param number the number of characters to be read again.
178 | * This number must not be greater than yylength()!
179 | */
180 | --- yypushback decl (contains zzScanError exception)
181 | if ( number > yylength() )
182 | zzScanError(ZZ_PUSHBACK_2BIG);
183 |
184 | zzMarkedPos -= number;
185 | }
186 |
187 |
188 | --- zzDoEOF
189 | /**
190 | * Resumes scanning until the next regular expression is matched,
191 | * the end of input is encountered or an I/O-Error occurs.
192 | *
193 | * @return the next token
194 | * @exception java.io.IOException if any I/O-Error occurs
195 | */
196 | --- yylex declaration
197 | int zzInput;
198 | int zzAction;
199 |
200 | // cached fields:
201 | int zzCurrentPosL;
202 | int zzMarkedPosL;
203 | int zzEndReadL = zzEndRead;
204 | CharSequence zzBufferL = zzBuffer;
205 | char[] zzBufferArrayL = zzBufferArray;
206 | char [] zzCMapL = ZZ_CMAP;
207 |
208 | --- local declarations
209 |
210 | while (true) {
211 | zzMarkedPosL = zzMarkedPos;
212 |
213 | --- start admin (line, char, col count)
214 | zzAction = -1;
215 |
216 | zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
217 |
218 | --- start admin (lexstate etc)
219 |
220 | zzForAction: {
221 | while (true) {
222 |
223 | --- next input, line, col, char count, next transition, isFinal action
224 | zzAction = zzState;
225 | zzMarkedPosL = zzCurrentPosL;
226 | --- line count update
227 | }
228 |
229 | }
230 | }
231 |
232 | // store back cached position
233 | zzMarkedPos = zzMarkedPosL;
234 | --- char count update
235 |
236 | --- actions
237 | default:
238 | if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
239 | zzAtEOF = true;
240 | --- eofvalue
241 | }
242 | else {
243 | --- no match
244 | }
245 | }
246 | }
247 | }
248 |
249 | --- main
250 |
251 | }
252 |
--------------------------------------------------------------------------------
/pycharm-pyxl.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/pycharm-pyxl.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/christoffer/pycharm-pyxl/2c7cbe56196307923aa0c4eb8fd54aa067e16d85/pycharm-pyxl.jar
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/PyxlColorSettingsPage.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl;
2 |
3 | import com.intellij.openapi.editor.colors.TextAttributesKey;
4 | import com.intellij.openapi.fileTypes.SyntaxHighlighter;
5 | import com.intellij.openapi.options.colors.AttributesDescriptor;
6 | import com.intellij.openapi.options.colors.ColorDescriptor;
7 | import com.intellij.openapi.options.colors.ColorSettingsPage;
8 | import com.jetbrains.python.PythonFileType;
9 | import com.jetbrains.python.psi.LanguageLevel;
10 | import org.jetbrains.annotations.NotNull;
11 | import org.jetbrains.annotations.Nullable;
12 |
13 | import javax.swing.*;
14 | import java.util.Map;
15 |
16 | public class PyxlColorSettingsPage implements ColorSettingsPage {
17 | private static final AttributesDescriptor[] COLOR_ATTRIBUTES = new AttributesDescriptor[]{
18 | new AttributesDescriptor("Tag name", PyxlHighlighterColors.PYXL_TAG_NAME),
19 | new AttributesDescriptor("Tag", PyxlHighlighterColors.PYXL_TAG),
20 | new AttributesDescriptor("Attribute name", PyxlHighlighterColors.PYXL_ATTRIBUTE_NAME),
21 | new AttributesDescriptor("Attribute value", PyxlHighlighterColors.PYXL_ATTRIBUTE_VALUE),
22 | new AttributesDescriptor("Embedded Python", PyxlHighlighterColors.PYXL_EMBEDDED),
23 | };
24 |
25 | @Nullable
26 | @Override
27 | public Icon getIcon() {
28 | return PythonFileType.INSTANCE.getIcon();
29 | }
30 |
31 | @NotNull
32 | @Override
33 | public SyntaxHighlighter getHighlighter() {
34 | return new PyxlHighlighter(LanguageLevel.getDefault());
35 | }
36 |
37 | @NotNull
38 | @Override
39 | public String getDemoText() {
40 | return "# coding: pyxl\n" +
41 | "\n" +
42 | "from pyxl import html\n" +
43 | "\n" +
44 | "def generate():\n" +
45 | " markup = (\n" +
46 | " \n" +
47 | " \n" +
48 | " \n" +
49 | " Hello {user.get_name()}!\n" +
50 | " \n" +
51 | " \n" +
52 | " )\n" +
53 | " return {markup}
";
54 | }
55 |
56 | @Nullable
57 | @Override
58 | public Map getAdditionalHighlightingTagToDescriptorMap() {
59 | return null;
60 | }
61 |
62 | @NotNull
63 | @Override
64 | public AttributesDescriptor[] getAttributeDescriptors() {
65 | return COLOR_ATTRIBUTES;
66 | }
67 |
68 | @NotNull
69 | @Override
70 | public ColorDescriptor[] getColorDescriptors() {
71 | return ColorDescriptor.EMPTY_ARRAY;
72 | }
73 |
74 | @NotNull
75 | @Override
76 | public String getDisplayName() {
77 | return "Pyxl Colors";
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/PyxlDialectTokenContributor.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl;
2 |
3 | import com.intellij.psi.tree.TokenSet;
4 | import com.jetbrains.python.PythonDialectsTokenSetContributor;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | public class PyxlDialectTokenContributor implements PythonDialectsTokenSetContributor {
8 | @NotNull
9 | @Override
10 | public TokenSet getStatementTokens() {
11 | return TokenSet.EMPTY;
12 | }
13 |
14 | @NotNull
15 | @Override
16 | public TokenSet getExpressionTokens() {
17 | return TokenSet.create(PyxlElementTypes.TAG, PyxlElementTypes.MODULE_REFERENCE);
18 | }
19 |
20 | @NotNull
21 | @Override
22 | public TokenSet getKeywordTokens() {
23 | return TokenSet.EMPTY;
24 | }
25 |
26 | @NotNull
27 | @Override
28 | public TokenSet getParameterTokens() {
29 | return TokenSet.EMPTY;
30 | }
31 |
32 | @NotNull
33 | @Override
34 | public TokenSet getFunctionDeclarationTokens() {
35 | return TokenSet.EMPTY;
36 | }
37 |
38 | @NotNull
39 | @Override
40 | public TokenSet getUnbalancedBracesRecoveryTokens() {
41 | return TokenSet.EMPTY;
42 | }
43 |
44 | @NotNull
45 | @Override
46 | public TokenSet getReferenceExpressionTokens() {
47 | return TokenSet.EMPTY;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/PyxlElementTypes.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl;
2 |
3 | import com.christofferklang.pyxl.psi.PythonClassReference;
4 | import com.christofferklang.pyxl.psi.PyxlArgumentList;
5 | import com.christofferklang.pyxl.psi.PyxlAttrName;
6 | import com.christofferklang.pyxl.psi.PyxlTag;
7 | import com.intellij.psi.tree.IElementType;
8 | import com.jetbrains.python.psi.PyElementType;
9 |
10 | public class PyxlElementTypes {
11 | public static IElementType TAG_REFERENCE =
12 | new PyElementType("TAG_REFERENCE", PythonClassReference.class);
13 |
14 | public static IElementType TAG =
15 | new PyElementType("TAG", PyxlTag.class);
16 |
17 | public static IElementType ATTRNAME =
18 | new PyElementType("ATTRNAME", PyxlAttrName.class);
19 |
20 | public static IElementType ARGUMENT_LIST =
21 | new PyElementType("PYXL_ARGUMENT_LIST", PyxlArgumentList.class);
22 |
23 | public static IElementType MODULE_REFERENCE = new PyElementType("MODULE_REFERENCE", PyxlModuleReference.class);
24 | }
25 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/PyxlFormattingModelBuilder.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl;
2 | import com.intellij.formatting.SpacingBuilder;
3 | import com.intellij.psi.codeStyle.CodeStyleSettings;
4 | import com.jetbrains.python.formatter.PythonFormattingModelBuilder;
5 |
6 | public class PyxlFormattingModelBuilder extends PythonFormattingModelBuilder {
7 |
8 | //we should be able to add Pyxl indentation information here. See also
9 | // http://confluence.jetbrains.com/display/IDEADEV/Developing+Custom+Language+Plugins+for+IntelliJ+IDEA#DevelopingCustomLanguagePluginsforIntelliJIDEA-CodeFormatter
10 |
11 | public PyxlFormattingModelBuilder() {
12 | super();
13 | }
14 |
15 |
16 | @Override
17 | protected SpacingBuilder createSpacingBuilder(CodeStyleSettings settings) {
18 | SpacingBuilder foo = super.createSpacingBuilder(settings);
19 | return foo;
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/PyxlHighlighter.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl;
2 |
3 | import com.christofferklang.pyxl.parsing.PyxlHighlightingLexer;
4 | import com.intellij.lexer.Lexer;
5 | import com.intellij.openapi.editor.colors.TextAttributesKey;
6 | import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
7 | import com.intellij.psi.tree.IElementType;
8 | import com.jetbrains.python.highlighting.PyHighlighter;
9 | import com.jetbrains.python.psi.LanguageLevel;
10 | import gnu.trove.THashMap;
11 | import org.jetbrains.annotations.NotNull;
12 |
13 | import java.util.Map;
14 |
15 | class PyxlHighlighter extends PyHighlighter {
16 | private static final Map keys1;
17 | private static final Map keys2;
18 | private final LanguageLevel myLanguageLevel;
19 |
20 | public PyxlHighlighter(LanguageLevel languageLevel) {
21 | super(languageLevel);
22 | myLanguageLevel = languageLevel;
23 | }
24 |
25 | @NotNull
26 | @Override
27 | public Lexer getHighlightingLexer() {
28 | return new PyxlHighlightingLexer(myLanguageLevel);
29 | }
30 |
31 | static {
32 | keys1 = new THashMap();
33 | keys2 = new THashMap();
34 |
35 | keys1.put(PyxlTokenTypes.TAGBEGIN, PyxlHighlighterColors.PYXL_TAG);
36 | keys1.put(PyxlTokenTypes.TAGCLOSE, PyxlHighlighterColors.PYXL_TAG);
37 | keys1.put(PyxlTokenTypes.TAGEND, PyxlHighlighterColors.PYXL_TAG);
38 | keys1.put(PyxlTokenTypes.TAGENDANDCLOSE, PyxlHighlighterColors.PYXL_TAG);
39 |
40 | keys1.put(PyxlElementTypes.TAG, PyxlHighlighterColors.PYXL_EMBEDDED);
41 |
42 | keys1.put(PyxlTokenTypes.TAGNAME, PyxlHighlighterColors.PYXL_TAG_NAME);
43 | keys1.put(PyxlTokenTypes.TAGNAME_MODULE, PyxlHighlighterColors.PYXL_TAG_NAME);
44 | keys1.put(PyxlTokenTypes.BUILT_IN_TAG, PyxlHighlighterColors.PYXL_TAG_NAME);
45 |
46 | keys1.put(PyxlTokenTypes.ATTRNAME, PyxlHighlighterColors.PYXL_ATTRIBUTE_NAME);
47 | keys1.put(PyxlTokenTypes.ATTRVALUE, PyxlHighlighterColors.PYXL_ATTRIBUTE_VALUE);
48 | }
49 |
50 | @NotNull
51 | @Override
52 | public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {
53 | TextAttributesKey[] defaultTextAttributeKeys, extendedHighlighterKeys;
54 |
55 | defaultTextAttributeKeys = super.getTokenHighlights(tokenType);
56 | extendedHighlighterKeys = SyntaxHighlighterBase.pack(keys1.get(tokenType), keys2.get(tokenType));
57 |
58 | int numKeys = defaultTextAttributeKeys.length + extendedHighlighterKeys.length;
59 | TextAttributesKey[] merged = new TextAttributesKey[numKeys];
60 |
61 | System.arraycopy(defaultTextAttributeKeys, 0, merged, 0, defaultTextAttributeKeys.length);
62 | System.arraycopy(extendedHighlighterKeys,
63 | 0, merged,
64 | defaultTextAttributeKeys.length,
65 | extendedHighlighterKeys.length);
66 |
67 | return merged;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/PyxlHighlighterColors.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl;
2 |
3 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
4 | import com.intellij.openapi.editor.XmlHighlighterColors;
5 | import com.intellij.openapi.editor.colors.TextAttributesKey;
6 |
7 | public class PyxlHighlighterColors {
8 | private PyxlHighlighterColors() {
9 | // Prevent instantiation
10 | }
11 |
12 | public static final TextAttributesKey PYXL_TAG_NAME = TextAttributesKey.createTextAttributesKey("PYXL_TAG_NAME", XmlHighlighterColors.HTML_TAG_NAME);
13 | public static final TextAttributesKey PYXL_TAG = TextAttributesKey.createTextAttributesKey("PYXL_TAG", XmlHighlighterColors.HTML_TAG);
14 | public static final TextAttributesKey PYXL_ATTRIBUTE_NAME = TextAttributesKey.createTextAttributesKey("PYXL_ATTRIBUTE_NAME", XmlHighlighterColors.HTML_ATTRIBUTE_NAME);
15 | public static final TextAttributesKey PYXL_ATTRIBUTE_VALUE = TextAttributesKey.createTextAttributesKey("PYXL_ATTRIBUTE_VALUE", XmlHighlighterColors.HTML_ATTRIBUTE_VALUE);
16 | public static final TextAttributesKey PYXL_EMBEDDED = TextAttributesKey.createTextAttributesKey("PYXL_EMBEDDED", DefaultLanguageHighlighterColors.TEMPLATE_LANGUAGE_COLOR);
17 | }
18 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/PyxlHighlighterFactory.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl;
2 |
3 | import com.intellij.openapi.fileTypes.SyntaxHighlighter;
4 | import com.intellij.openapi.project.Project;
5 | import com.intellij.openapi.vfs.VirtualFile;
6 | import com.jetbrains.python.highlighting.PySyntaxHighlighterFactory;
7 | import com.jetbrains.python.psi.LanguageLevel;
8 | import com.jetbrains.python.psi.PyFile;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | public class PyxlHighlighterFactory extends PySyntaxHighlighterFactory {
12 | @NotNull
13 | @Override
14 | public SyntaxHighlighter getSyntaxHighlighter(Project project, VirtualFile virtualFile) {
15 | return new PyxlHighlighter(getLanguageLevelForFile(virtualFile));
16 | }
17 |
18 | private LanguageLevel getLanguageLevelForFile(VirtualFile virtualFile) {
19 | if (virtualFile instanceof PyFile) {
20 | return ((PyFile) virtualFile).getLanguageLevel();
21 | }
22 | return LanguageLevel.getDefault();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/PyxlModuleReference.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl;
2 |
3 | import com.intellij.lang.ASTNode;
4 | import com.jetbrains.python.psi.impl.PyReferenceExpressionImpl;
5 | import org.jetbrains.annotations.Nullable;
6 |
7 | public class PyxlModuleReference extends PyReferenceExpressionImpl {
8 | public PyxlModuleReference(ASTNode astNode) {
9 | super(astNode);
10 | }
11 |
12 | @Nullable
13 | @Override
14 | public ASTNode getNameElement() {
15 | return getNode().findChildByType(PyxlTokenTypes.TAGNAME_MODULE);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/PyxlTokenTypes.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl;
2 |
3 | import com.jetbrains.python.psi.PyElementType;
4 |
5 | public class PyxlTokenTypes {
6 | private PyxlTokenTypes() {
7 | // Prevent instantiation
8 | }
9 |
10 | public static final PyElementType BADCHAR = new PyElementType("PYXL BAD CHAR");
11 | public static final PyElementType ATTRNAME = new PyElementType("PYXL ATTRNAME");
12 | public static final PyElementType ATTRVALUE = new PyElementType("PYXL ATTRVALUE");
13 | public static final PyElementType ATTRVALUE_START = new PyElementType("PYXL ATTRVALUE BEGIN");
14 | public static final PyElementType ATTRVALUE_END = new PyElementType("PYXL ATTRVALUE END");
15 |
16 | public static final PyElementType TAGBEGIN = new PyElementType("PYXL TAGBEGIN <");
17 | public static final PyElementType TAGNAME_MODULE = new PyElementType("PYXL TAGNAME_MODULE");
18 | public static final PyElementType TAGNAME = new PyElementType("PYXL TAGNAME");
19 | public static final PyElementType TAGEND = new PyElementType("PYXL TAGEND >");
20 | public static final PyElementType TAGCLOSE = new PyElementType("PYXL TAGCLOSE ");
21 |
22 | public static final PyElementType TAGENDANDCLOSE = new PyElementType("PYXL TAGENDANDCLOSE />");
23 |
24 | public static final PyElementType BUILT_IN_TAG = new PyElementType("PYXL BUILT IN TAG");
25 |
26 | public static final PyElementType EMBED_START = new PyElementType("PYXL PYTHON EMBED BEGIN {");
27 | public static final PyElementType EMBED_END = new PyElementType("PYXL PYTHON EMBED END }");
28 | public static final PyElementType STRING = new PyElementType("PYXL STRING");
29 | }
30 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/parsing/Pyxl.flex:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.parsing;
2 |
3 | import com.intellij.lexer.FlexLexer;
4 | import com.intellij.psi.tree.IElementType;
5 | import com.jetbrains.python.PyTokenTypes;
6 | import com.christofferklang.pyxl.PyxlTokenTypes;
7 | import com.intellij.openapi.util.text.StringUtil;
8 | import java.util.Stack;
9 | import com.intellij.psi.tree.IElementType;
10 |
11 |
12 | // NOTE: JFlex lexer file is defined in http://www.jflex.de/manual.pdf
13 |
14 | %%
15 | // %debug uncomment for verbose output from the lexer
16 | %class _PyxlLexer
17 | %implements FlexLexer
18 | %unicode
19 | %function advance
20 | %type IElementType
21 |
22 | %eof{ return;
23 | %eof}
24 |
25 | DIGIT = [0-9]
26 | NONZERODIGIT = [1-9]
27 | OCTDIGIT = [0-7]
28 | HEXDIGIT = [0-9A-Fa-f]
29 | BINDIGIT = [01]
30 |
31 | HEXINTEGER = 0[Xx]({HEXDIGIT})+
32 | OCTINTEGER = 0[Oo]?({OCTDIGIT})+
33 | BININTEGER = 0[Bb]({BINDIGIT})+
34 | DECIMALINTEGER = (({NONZERODIGIT}({DIGIT})*)|0)
35 | INTEGER = {DECIMALINTEGER}|{OCTINTEGER}|{HEXINTEGER}|{BININTEGER}
36 | LONGINTEGER = {INTEGER}[Ll]
37 |
38 | END_OF_LINE_COMMENT="#"[^\r\n]*
39 | PYXL_ENCODING_STRING = "#"{S}"coding:"{S}[^\n\r]*
40 | IDENT_START = [a-zA-Z_]|[:unicode_uppercase_letter:]|[:unicode_lowercase_letter:]|[:unicode_titlecase_letter:]|[:unicode_modifier_letter:]|[:unicode_other_letter:]|[:unicode_letter_number:]
41 | IDENT_CONTINUE = [a-zA-Z0-9_]|[:unicode_uppercase_letter:]|[:unicode_lowercase_letter:]|[:unicode_titlecase_letter:]|[:unicode_modifier_letter:]|[:unicode_other_letter:]|[:unicode_letter_number:]|[:unicode_non_spacing_mark:]|[:unicode_combining_spacing_mark:]|[:unicode_decimal_digit_number:]|[:unicode_connector_punctuation:]
42 | IDENTIFIER = {IDENT_START}{IDENT_CONTINUE}**
43 |
44 | FLOATNUMBER=({POINTFLOAT})|({EXPONENTFLOAT})
45 | POINTFLOAT=(({INTPART})?{FRACTION})|({INTPART}\.)
46 | EXPONENTFLOAT=(({INTPART})|({POINTFLOAT})){EXPONENT}
47 | INTPART = ({DIGIT})+
48 | FRACTION = \.({DIGIT})+
49 | EXPONENT = [eE][+\-]?({DIGIT})+
50 |
51 | IMAGNUMBER=(({FLOATNUMBER})|({INTPART}))[Jj]
52 |
53 | SINGLE_QUOTED_STRING=[UuBbCcRr]{0,2}({QUOTED_LITERAL} | {DOUBLE_QUOTED_LITERAL})
54 | TRIPLE_QUOTED_STRING=[UuBbCcRr]{0,2}[UuBbCcRr]?({TRIPLE_QUOTED_LITERAL}|{TRIPLE_APOS_LITERAL})
55 |
56 | DOCSTRING_LITERAL=({SINGLE_QUOTED_STRING}|{TRIPLE_QUOTED_STRING})
57 |
58 | QUOTED_LITERAL="'" ([^\\\'\r\n] | {ESCAPE_SEQUENCE} | (\\[\r\n]))* ("'"|\\)?
59 | DOUBLE_QUOTED_LITERAL=\"([^\\\"\r\n]|{ESCAPE_SEQUENCE}|(\\[\r\n]))*?(\"|\\)?
60 | ESCAPE_SEQUENCE=\\[^\r\n]
61 |
62 | ANY_ESCAPE_SEQUENCE = \\[^]
63 |
64 | THREE_QUO = (\"\"\")
65 | ONE_TWO_QUO = (\"[^\"]) | (\"\\[^]) | (\"\"[^\"]) | (\"\"\\[^])
66 | QUO_STRING_CHAR = [^\\\"] | {ANY_ESCAPE_SEQUENCE} | {ONE_TWO_QUO}
67 | TRIPLE_QUOTED_LITERAL = {THREE_QUO} {QUO_STRING_CHAR}* {THREE_QUO}?
68 |
69 | THREE_APOS = (\'\'\')
70 | ONE_TWO_APOS = ('[^']) | ('\\[^]) | (''[^']) | (''\\[^])
71 | APOS_STRING_CHAR = [^\\'] | {ANY_ESCAPE_SEQUENCE} | {ONE_TWO_APOS}
72 | TRIPLE_APOS_LITERAL = {THREE_APOS} {APOS_STRING_CHAR}* {THREE_APOS}?
73 |
74 |
75 | S = [\ \t\n]*
76 | PYXL_TAGNAME = {IDENT_START}[a-zA-Z0-9_]**
77 | _ATTR_PART = {IDENT_START}[a-zA-Z0-9_-]**
78 | PYXL_ATTRNAME = ({_ATTR_PART}":")?{_ATTR_PART}**
79 | PYXL_PRE_OP = [\=\(\[\{,\:\>] // matching tokenizer.py in dropbox's pyxl parser
80 | PYXL_PRE_KEYWD = (print|else|yield|return)
81 | PYXL_TAG_COMING = ({PYXL_PRE_OP}|{PYXL_PRE_KEYWD}){S}"<"
82 | PYXL_TAGCLOSE = "" ({IDENTIFIER}".")*{PYXL_TAGNAME} ">"
83 | PYXL_COMMENT = ""
84 | PYXL_DOCTYPE = "])* ">"
85 | PYXL_CDATA = "]))* "]]>"
86 |
87 | // attribute value insides. Includes support for line-continuation both by keeping quotes open and using '\' marker
88 | // at EOL. That seems strange but i've seen examples of both in our code.
89 | PYXL_ATTRVAL_UNQUOTED_CHAR=[^\"\'\`=\<\>{ \r\n]
90 | PYXL_ATTRVALUE_2Q = ([^\\\"{]|{ESCAPE_SEQUENCE}|(\\[\r\n]))*?
91 | PYXL_ATTRVALUE_1Q = ([^\\'{]|{ESCAPE_SEQUENCE}|(\\[\r\n]))*?
92 |
93 | // a string in a pyxl block, outside tags and quotes (can't contain {} <> # etc)
94 | PYXL_BLOCK_STRING = ([^<{#])*?
95 |
96 | %state IN_PYXL_DOCUMENT
97 | %state IN_PYXL_BLOCK
98 | %state IN_PYXL_TAG_NAME
99 | %state IN_PYXL_PYTHON_EMBED
100 | %state PENDING_PYXL_TAG_FROM_PYTHON
101 | %state PENDING_PYXL_TAG_FROM_PYXL
102 | %state ATTR_VALUE_UNQUOTED
103 | %state ATTR_VALUE_1Q
104 | %state ATTR_VALUE_2Q
105 | %state IN_ATTR
106 | %state IN_ATTRVALUE
107 | %state IN_CLOSE_TAG
108 |
109 | %{
110 | private void enterState(int state) {
111 | stateStack.push(new State(zzLexicalState, embedBraceCount));
112 | yybegin(state);
113 | embedBraceCount = 0;
114 | }
115 | private boolean exitState() {
116 | int size = stateStack.size();
117 | if (size <= 0) {
118 | yybegin(YYINITIAL);
119 | return false; // error condition
120 | } else {
121 | State mystate = stateStack.pop();
122 | yybegin(mystate.lexState);
123 | embedBraceCount = mystate.embedBraceCount;
124 | return true;
125 | }
126 | }
127 |
128 | // Counter for keeping track of when an embed statement ends, as opposed to when inner braces closes.
129 | int embedBraceCount = 0;
130 |
131 | class State {
132 | public int lexState;
133 | public int embedBraceCount;
134 |
135 | State (int lexState, int embedBraceCount) {
136 | this.lexState = lexState;
137 | this.embedBraceCount = embedBraceCount;
138 | }
139 | }
140 | Stack stateStack = new Stack();
141 |
142 | %}
143 |
144 | %state PENDING_DOCSTRING
145 | %state IN_DOCSTRING_OWNER
146 | %{
147 |
148 | private int getSpaceLength(CharSequence string) {
149 | String string1 = string.toString();
150 | string1 = StringUtil.trimEnd(string1, "\\");
151 | string1 = StringUtil.trimEnd(string1, ";");
152 | final String s = StringUtil.trimTrailing(string1);
153 | return yylength()-s.length();
154 |
155 | }
156 |
157 | %}
158 | %%
159 |
160 | [\ ] { return PyTokenTypes.SPACE; }
161 | [\t] { return PyTokenTypes.TAB; }
162 | [\f] { return PyTokenTypes.FORMFEED; }
163 | "\\" { return PyTokenTypes.BACKSLASH; }
164 |
165 | {
166 | {SINGLE_QUOTED_STRING} { return PyTokenTypes.SINGLE_QUOTED_STRING; }
167 | {TRIPLE_QUOTED_STRING} { return PyTokenTypes.TRIPLE_QUOTED_STRING; }
168 | "{" { embedBraceCount++; return PyTokenTypes.LBRACE; }
169 | "}" { if (embedBraceCount-- == 0) {
170 | exitState();
171 | return PyxlTokenTypes.EMBED_END;
172 | } else {
173 | return PyTokenTypes.RBRACE;
174 | }
175 | }
176 | // remainder of python is defined below in the python states.
177 | }
178 |
179 | // look for pyxl tag starts in python contexts using a lookahead
180 | {
181 | {PYXL_TAG_COMING} {
182 | yypushback(yylength());
183 | enterState(PENDING_PYXL_TAG_FROM_PYTHON);
184 | }
185 | }
186 |
187 | // look for pyxl tag starts in pyxl contexts
188 | {
189 | "<" {
190 | if (zzLexicalState == PENDING_PYXL_TAG_FROM_PYTHON) {
191 | yybegin(IN_PYXL_TAG_NAME);
192 | } else {
193 | enterState(IN_PYXL_TAG_NAME);
194 | }
195 | return PyxlTokenTypes.TAGBEGIN;
196 | }
197 | }
198 |
199 | {
200 | "if" { return PyxlTokenTypes.BUILT_IN_TAG; }
201 | "else" { return PyxlTokenTypes.BUILT_IN_TAG; }
202 | {IDENTIFIER}"." { yypushback(1); return PyxlTokenTypes.TAGNAME_MODULE; }
203 | {PYXL_TAGNAME} { return PyxlTokenTypes.TAGNAME; }
204 | ">" { return exitState() ? PyxlTokenTypes.TAGEND : PyxlTokenTypes.BADCHAR; }
205 | "." { return PyTokenTypes.DOT; }
206 | . { return PyxlTokenTypes.BADCHAR; }
207 | }
208 |
209 | {
210 | // TODO(christoffer) Proper handling of inner Python code
211 | // In the interest of keeping the Lexer simple; I've taken a little shortcut for
212 | // comments, doctypes and cdata in that I don't consider the case where inner Python code can
213 | // contain the end delimiter, as this is likely to be an edge case.
214 | //
215 | // Here's how Pyxl behaves with Python sections containing the end delimiter inside them:
216 | //
217 | // "}> (output: ``, Python is ignored)
218 | // "}]]> (output: `]]>`, the Python is output *before* the CDATA?)
219 | // "}--> (output: ``, Python is ignored)
220 | //
221 | // We don't need a Python state for the inner Python code for DOCTYPE and COMMENT since it's
222 | // ignored anyway (it's not even executed), and since the Python behavior looks a bit undefined
223 | // for the CDATA case, I'm betting it's safe to ignore Python handling in there as well.
224 | //
225 | // Note however that the lexer currently will prematurely end the state in each
226 | // of the above examples.
227 | {PYXL_CDATA} { return PyTokenTypes.END_OF_LINE_COMMENT; }
228 | {PYXL_DOCTYPE} { return PyTokenTypes.END_OF_LINE_COMMENT; }
229 | {PYXL_COMMENT} { return PyTokenTypes.END_OF_LINE_COMMENT; }
230 | "{" { enterState(IN_PYXL_PYTHON_EMBED); return PyxlTokenTypes.EMBED_START; }
231 | {PYXL_TAGCLOSE} { yybegin(IN_CLOSE_TAG); yypushback(yylength()-2); return PyxlTokenTypes.TAGCLOSE; }
232 | {END_OF_LINE_COMMENT} { return PyTokenTypes.END_OF_LINE_COMMENT; }
233 | {PYXL_BLOCK_STRING} { return PyxlTokenTypes.STRING; }
234 | . { return PyxlTokenTypes.BADCHAR; }
235 | }
236 |
237 | // HTML allows attribute values without quotes, if they conform to a limited set of characters.
238 | {
239 | {PYXL_ATTRVAL_UNQUOTED_CHAR}* { exitState(); return PyxlTokenTypes.ATTRVALUE; }
240 | . { return PyxlTokenTypes.BADCHAR;}
241 | }
242 |
243 | {
244 | "'" { exitState(); return PyxlTokenTypes.ATTRVALUE_END; } // end of attribute value
245 | {PYXL_ATTRVALUE_1Q} { return PyxlTokenTypes.ATTRVALUE;}
246 | "{" { enterState(IN_PYXL_PYTHON_EMBED); return PyxlTokenTypes.EMBED_START; }
247 | . { return PyxlTokenTypes.BADCHAR;}
248 | }
249 |
250 | {
251 | "\"" { exitState(); return PyxlTokenTypes.ATTRVALUE_END;} // end of attribute value
252 | {PYXL_ATTRVALUE_2Q} { return PyxlTokenTypes.ATTRVALUE;}
253 | "{" { enterState(IN_PYXL_PYTHON_EMBED); return PyxlTokenTypes.EMBED_START; }
254 | [^] { return PyxlTokenTypes.BADCHAR;}
255 |
256 | }
257 |
258 | { // parse an attribute value
259 | "'" { yybegin(ATTR_VALUE_1Q); return PyxlTokenTypes.ATTRVALUE_START; }
260 | "\"" { yybegin(ATTR_VALUE_2Q); return PyxlTokenTypes.ATTRVALUE_START; }
261 | {PYXL_ATTRVAL_UNQUOTED_CHAR} { yypushback(1); yybegin(ATTR_VALUE_UNQUOTED); }
262 | // python embed without quotes
263 | "{" { yybegin(IN_PYXL_PYTHON_EMBED); return PyxlTokenTypes.EMBED_START; }
264 | [^] { return PyxlTokenTypes.BADCHAR; }
265 |
266 | }
267 | { // parse an attribute name and value
268 | {PYXL_ATTRNAME} { return PyxlTokenTypes.ATTRNAME; }
269 | "=" { enterState(IN_ATTRVALUE); return PyTokenTypes.EQ; }
270 |
271 | ">" { yybegin(IN_PYXL_BLOCK); return PyxlTokenTypes.TAGEND;}
272 | "/>" { return exitState() ? PyxlTokenTypes.TAGENDANDCLOSE : PyxlTokenTypes.BADCHAR; }
273 | {END_OF_LINE_COMMENT} { return PyTokenTypes.END_OF_LINE_COMMENT; }
274 | . { return PyxlTokenTypes.BADCHAR; }
275 | }
276 |
277 | { // parse a tag name
278 | //">" { yybegin(IN_PYXL_BLOCK); return PyxlTokenTypes.TAGEND; }
279 | "if" { yybegin(IN_ATTR); return PyxlTokenTypes.BUILT_IN_TAG; }
280 | "else" { yybegin(IN_ATTR); return PyxlTokenTypes.BUILT_IN_TAG; }
281 | {IDENTIFIER}"." { yypushback(1); return PyxlTokenTypes.TAGNAME_MODULE; }
282 | {PYXL_TAGNAME} { yybegin(IN_ATTR); return PyxlTokenTypes.TAGNAME; }
283 | "." { return PyTokenTypes.DOT; }
284 | . { return PyxlTokenTypes.BADCHAR; }
285 | }
286 |
287 | {
288 | ":"(\ )*{END_OF_LINE_COMMENT}?"\n" { yypushback(yylength()-1); enterState(PENDING_DOCSTRING); return PyTokenTypes.COLON; }
289 | }
290 |
291 | {
292 | {SINGLE_QUOTED_STRING} { if (zzInput == YYEOF) return PyTokenTypes.DOCSTRING;
293 | else exitState(); return PyTokenTypes.SINGLE_QUOTED_STRING; }
294 | {TRIPLE_QUOTED_STRING} { if (zzInput == YYEOF) return PyTokenTypes.DOCSTRING;
295 | else exitState(); return PyTokenTypes.TRIPLE_QUOTED_STRING; }
296 | {DOCSTRING_LITERAL}[\ \t]*[\n;] { yypushback(getSpaceLength(yytext())); exitState(); return PyTokenTypes.DOCSTRING; }
297 | {DOCSTRING_LITERAL}[\ \t]*"\\" { yypushback(getSpaceLength(yytext())); return PyTokenTypes.DOCSTRING; }
298 |
299 | . { yypushback(1); exitState(); }
300 | }
301 |
302 | // NOTE(christoffer): Must be above YYINITIAL:{END_OF_LINE_COMMENT} as the length is identical, and
303 | // this must match before.
304 | {
305 | {PYXL_ENCODING_STRING} { // Look for # coding: pyxl
306 | if(zzCurrentPos == 0) {
307 | enterState(IN_PYXL_DOCUMENT);
308 | return PyTokenTypes.END_OF_LINE_COMMENT;
309 | }
310 | }
311 | }
312 |
313 | {
314 | [\n] { if (zzCurrentPos == 0) enterState(PENDING_DOCSTRING); return PyTokenTypes.LINE_BREAK; }
315 | {END_OF_LINE_COMMENT} { if (zzCurrentPos == 0) enterState(PENDING_DOCSTRING); return PyTokenTypes.END_OF_LINE_COMMENT; }
316 |
317 | {SINGLE_QUOTED_STRING} { if (zzInput == YYEOF && zzStartRead == 0) return PyTokenTypes.DOCSTRING;
318 | else return PyTokenTypes.SINGLE_QUOTED_STRING; }
319 | {TRIPLE_QUOTED_STRING} { if (zzInput == YYEOF && zzStartRead == 0) return PyTokenTypes.DOCSTRING;
320 | else return PyTokenTypes.TRIPLE_QUOTED_STRING; }
321 |
322 | {SINGLE_QUOTED_STRING}[\ \t]*[\n;] { yypushback(getSpaceLength(yytext())); if (zzCurrentPos != 0) return PyTokenTypes.SINGLE_QUOTED_STRING;
323 | return PyTokenTypes.DOCSTRING; }
324 |
325 | {TRIPLE_QUOTED_STRING}[\ \t]*[\n;] { yypushback(getSpaceLength(yytext())); if (zzCurrentPos != 0) return PyTokenTypes.TRIPLE_QUOTED_STRING;
326 | return PyTokenTypes.DOCSTRING; }
327 |
328 | {SINGLE_QUOTED_STRING}[\ \t]*"\\" {
329 | yypushback(getSpaceLength(yytext())); if (zzCurrentPos != 0) return PyTokenTypes.SINGLE_QUOTED_STRING;
330 | enterState(PENDING_DOCSTRING); return PyTokenTypes.DOCSTRING; }
331 |
332 | {TRIPLE_QUOTED_STRING}[\ \t]*"\\" {
333 | yypushback(getSpaceLength(yytext())); if (zzCurrentPos != 0) return PyTokenTypes.TRIPLE_QUOTED_STRING;
334 | enterState(PENDING_DOCSTRING); return PyTokenTypes.DOCSTRING; }
335 |
336 | }
337 |
338 | [\n] { return PyTokenTypes.LINE_BREAK; }
339 |
340 | // python states
341 | {
342 |
343 | // this rule was for ALL states in python; with Pyxl addition we have to limit it to python states only.
344 | {END_OF_LINE_COMMENT} { return PyTokenTypes.END_OF_LINE_COMMENT; }
345 |
346 | {LONGINTEGER} { return PyTokenTypes.INTEGER_LITERAL; }
347 | {INTEGER} { return PyTokenTypes.INTEGER_LITERAL; }
348 | {FLOATNUMBER} { return PyTokenTypes.FLOAT_LITERAL; }
349 | {IMAGNUMBER} { return PyTokenTypes.IMAGINARY_LITERAL; }
350 |
351 | {SINGLE_QUOTED_STRING} { return PyTokenTypes.SINGLE_QUOTED_STRING; }
352 | {TRIPLE_QUOTED_STRING} { return PyTokenTypes.TRIPLE_QUOTED_STRING; }
353 |
354 | "and" { return PyTokenTypes.AND_KEYWORD; }
355 | "assert" { return PyTokenTypes.ASSERT_KEYWORD; }
356 | "break" { return PyTokenTypes.BREAK_KEYWORD; }
357 | "class" { yybegin(IN_DOCSTRING_OWNER); return PyTokenTypes.CLASS_KEYWORD; }
358 | "continue" { return PyTokenTypes.CONTINUE_KEYWORD; }
359 | "def" { yybegin(IN_DOCSTRING_OWNER); return PyTokenTypes.DEF_KEYWORD; }
360 | "del" { return PyTokenTypes.DEL_KEYWORD; }
361 | "elif" { return PyTokenTypes.ELIF_KEYWORD; }
362 | "else" { return PyTokenTypes.ELSE_KEYWORD; }
363 | "except" { return PyTokenTypes.EXCEPT_KEYWORD; }
364 | "finally" { return PyTokenTypes.FINALLY_KEYWORD; }
365 | "for" { return PyTokenTypes.FOR_KEYWORD; }
366 | "from" { return PyTokenTypes.FROM_KEYWORD; }
367 | "global" { return PyTokenTypes.GLOBAL_KEYWORD; }
368 | "if" { return PyTokenTypes.IF_KEYWORD; }
369 | "import" { return PyTokenTypes.IMPORT_KEYWORD; }
370 | "in" { return PyTokenTypes.IN_KEYWORD; }
371 | "is" { return PyTokenTypes.IS_KEYWORD; }
372 | "lambda" { return PyTokenTypes.LAMBDA_KEYWORD; }
373 | "not" { return PyTokenTypes.NOT_KEYWORD; }
374 | "or" { return PyTokenTypes.OR_KEYWORD; }
375 | "pass" { return PyTokenTypes.PASS_KEYWORD; }
376 | "print" { return PyTokenTypes.PRINT_KEYWORD; }
377 | "raise" { return PyTokenTypes.RAISE_KEYWORD; }
378 | "return" { return PyTokenTypes.RETURN_KEYWORD; }
379 | "try" { return PyTokenTypes.TRY_KEYWORD; }
380 | "while" { return PyTokenTypes.WHILE_KEYWORD; }
381 | "yield" { return PyTokenTypes.YIELD_KEYWORD; }
382 |
383 | {IDENTIFIER} { return PyTokenTypes.IDENTIFIER; }
384 |
385 | "+=" { return PyTokenTypes.PLUSEQ; }
386 | "-=" { return PyTokenTypes.MINUSEQ; }
387 | "**=" { return PyTokenTypes.EXPEQ; }
388 | "*=" { return PyTokenTypes.MULTEQ; }
389 | "//=" { return PyTokenTypes.FLOORDIVEQ; }
390 | "/=" { return PyTokenTypes.DIVEQ; }
391 | "%=" { return PyTokenTypes.PERCEQ; }
392 | "&=" { return PyTokenTypes.ANDEQ; }
393 | "|=" { return PyTokenTypes.OREQ; }
394 | "^=" { return PyTokenTypes.XOREQ; }
395 | ">>=" { return PyTokenTypes.GTGTEQ; }
396 | "<<=" { return PyTokenTypes.LTLTEQ; }
397 | "<<" { return PyTokenTypes.LTLT; }
398 | ">>" { return PyTokenTypes.GTGT; }
399 | "**" { return PyTokenTypes.EXP; }
400 | "//" { return PyTokenTypes.FLOORDIV; }
401 | "<=" { return PyTokenTypes.LE; }
402 | ">=" { return PyTokenTypes.GE; }
403 | "==" { return PyTokenTypes.EQEQ; }
404 | "!=" { return PyTokenTypes.NE; }
405 | "<>" { return PyTokenTypes.NE_OLD; }
406 | "+" { return PyTokenTypes.PLUS; }
407 | "-" { return PyTokenTypes.MINUS; }
408 | "*" { return PyTokenTypes.MULT; }
409 | "/" { return PyTokenTypes.DIV; }
410 | "%" { return PyTokenTypes.PERC; }
411 | "&" { return PyTokenTypes.AND; }
412 | "|" { return PyTokenTypes.OR; }
413 | "^" { return PyTokenTypes.XOR; }
414 | "~" { return PyTokenTypes.TILDE; }
415 | "<" { return PyTokenTypes.LT; }
416 | ">" { return PyTokenTypes.GT; }
417 | "(" { return PyTokenTypes.LPAR; }
418 | ")" { return PyTokenTypes.RPAR; }
419 | "[" { return PyTokenTypes.LBRACKET; }
420 | "]" { return PyTokenTypes.RBRACKET; }
421 | "{" { return PyTokenTypes.LBRACE; }
422 | "}" { return PyTokenTypes.RBRACE; }
423 | "@" { return PyTokenTypes.AT; }
424 | "," { return PyTokenTypes.COMMA; }
425 | ":" { return PyTokenTypes.COLON; }
426 |
427 | "." { return PyTokenTypes.DOT; }
428 | "`" { return PyTokenTypes.TICK; }
429 | "=" { return PyTokenTypes.EQ; }
430 | ";" { return PyTokenTypes.SEMICOLON; }
431 |
432 |
433 | . { return PyTokenTypes.BAD_CHARACTER; }
434 | }
435 |
436 | //[\n] { return PyTokenTypes.LINE_BREAK; }
437 | . { return PyxlTokenTypes.BADCHAR; }
438 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/parsing/PyxlExpressionParsing.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.parsing;
2 |
3 | import com.christofferklang.pyxl.PyxlElementTypes;
4 | import com.christofferklang.pyxl.PyxlTokenTypes;
5 | import com.intellij.lang.PsiBuilder;
6 | import com.intellij.psi.tree.IElementType;
7 | import com.jetbrains.python.PyElementTypes;
8 | import com.jetbrains.python.PyTokenTypes;
9 | import com.jetbrains.python.parsing.ExpressionParsing;
10 | import com.jetbrains.python.parsing.ParsingContext;
11 | import com.jetbrains.python.psi.PyElementType;
12 |
13 | import java.util.Arrays;
14 | import java.util.List;
15 |
16 | class PyxlExpressionParsing extends ExpressionParsing {
17 | private static class PyxlParsingException extends Throwable {
18 | public PyxlParsingException() {
19 | super();
20 | }
21 |
22 | public PyxlParsingException(String message) {
23 | super(message);
24 | }
25 | }
26 |
27 | private static final List PYXL_CLOSE_TOKENS =
28 | Arrays.asList(PyxlTokenTypes.TAGCLOSE); // , PyxlTokenTypes.IFTAGCLOSE);
29 |
30 | public PyxlExpressionParsing(ParsingContext context) {
31 | super(context);
32 | }
33 |
34 | public boolean parsePrimaryExpression(boolean isTargetExpression) {
35 | if (myBuilder.getTokenType() == PyxlTokenTypes.TAGBEGIN) {
36 | parsePyxlTag();
37 | return true;
38 | } else {
39 | return super.parsePrimaryExpression(isTargetExpression);
40 | }
41 | }
42 |
43 | /**
44 | * Parse a pyxl tag.
45 | */
46 | private void parsePyxlTag() {
47 | final PsiBuilder.Marker pyxl = myBuilder.mark();
48 | myBuilder.advanceLexer();
49 |
50 | String qualifiedName;
51 | try {
52 | qualifiedName = parseQualifiedPyxlTagName();
53 | } catch (PyxlParsingException e) {
54 | myBuilder.error(e.getMessage());
55 | pyxl.done(PyxlElementTypes.TAG);
56 | return;
57 | }
58 |
59 | if (!parsePyxlAttributes()) {
60 | pyxl.done(PyxlElementTypes.TAG);
61 | return;
62 | }
63 |
64 | IElementType token = myBuilder.getTokenType();
65 | if (token == PyxlTokenTypes.TAGENDANDCLOSE) {
66 | // The tag was self-closed ( /> ).
67 | final PsiBuilder.Marker argumentList = myBuilder.mark();
68 | argumentList.done(PyxlElementTypes.ARGUMENT_LIST);
69 | myBuilder.advanceLexer();
70 | } else if (token == PyxlTokenTypes.TAGEND) {
71 | // The tag has content (even empty content counts).
72 | myBuilder.advanceLexer();
73 |
74 | final PsiBuilder.Marker argumentList = myBuilder.mark();
75 |
76 | // Parse pyxl tag content.
77 | boolean error = false;
78 | while ((token = myBuilder.getTokenType()) != PyxlTokenTypes.TAGCLOSE) {
79 | // Parse embed expressions of the form {python_code}.
80 | try {
81 | if (parsePyxlEmbed()) {
82 | continue;
83 | }
84 | } catch (PyxlParsingException e) {
85 | error = true;
86 | break;
87 | }
88 |
89 | if (token == PyxlTokenTypes.TAGBEGIN) {
90 | parsePyxlTag();
91 | } else if (token == PyxlTokenTypes.STRING) {
92 | PsiBuilder.Marker stringLiteral = myBuilder.mark();
93 | myBuilder.advanceLexer();
94 | stringLiteral.done(PyElementTypes.STRING_LITERAL_EXPRESSION);
95 | } else {
96 | myBuilder.error(String.format("pyxl encountered unexpected token: %s", token));
97 | error = true;
98 | break;
99 | }
100 | }
101 |
102 | argumentList.done(PyElementTypes.ARGUMENT_LIST);
103 |
104 | if (error) {
105 | pyxl.done(PyxlElementTypes.TAG);
106 | return;
107 | }
108 |
109 | // Consume the token.
110 | myBuilder.advanceLexer();
111 |
112 | try {
113 | parseQualifiedPyxlTagName(qualifiedName, null, null);
114 | } catch (PyxlParsingException e) {
115 | pyxl.done(PyxlElementTypes.TAG);
116 | myBuilder.error(String.format("pyxl expected closing tag: %s>", qualifiedName));
117 | return;
118 | }
119 |
120 | if (myBuilder.getTokenType() == PyxlTokenTypes.TAGEND) {
121 | myBuilder.advanceLexer();
122 | } else {
123 | myBuilder.error("pyxl expected >");
124 | }
125 | }
126 | pyxl.done(PyxlElementTypes.TAG);
127 | }
128 |
129 | /**
130 | * Parses a pyxl tag name, including an optional list of module qualifiers.
131 | *
132 | * @return the full qualified name (e.g. "module1.module2.tag")
133 | * @throws PyxlParsingException if @requiredQualifiedName is non-null and doesn't match the parsed qualified name.
134 | */
135 | private String parseQualifiedPyxlTagName(String requiredQualifiedName,
136 | PsiBuilder.Marker callExpressionMarker,
137 | PsiBuilder.Marker tagStartMarker) throws PyxlParsingException {
138 | final IElementType token = myBuilder.getTokenType();
139 |
140 | String fullQualifiedName = myBuilder.getTokenText();
141 |
142 | // Module qualifiers are straight up python identifiers followed by a dot
143 | if (token == PyxlTokenTypes.TAGNAME_MODULE) {
144 | if (callExpressionMarker == null) callExpressionMarker = myBuilder.mark();
145 | if (tagStartMarker == null) tagStartMarker = myBuilder.mark();
146 | PsiBuilder.Marker moduleExpression = myBuilder.mark();
147 | myBuilder.advanceLexer();
148 | moduleExpression.done(PyxlElementTypes.MODULE_REFERENCE);
149 | if (myBuilder.getTokenType() != PyTokenTypes.DOT) {
150 | throw new PyxlParsingException();
151 | } else {
152 | myBuilder.advanceLexer();
153 | }
154 | fullQualifiedName = fullQualifiedName + "." +
155 | parseQualifiedPyxlTagName(null, callExpressionMarker, tagStartMarker);
156 | } else if (token == PyxlTokenTypes.TAGNAME) {
157 | // pyxl expands
to x_p()(x_c());
158 | // so in order to get the same semantics as the corresponding python would have, we fake a call to the init
159 | // function of the pyxl tag class here.
160 |
161 | if (callExpressionMarker == null) callExpressionMarker = myBuilder.mark();
162 | if (tagStartMarker == null) tagStartMarker = myBuilder.mark();
163 | myBuilder.advanceLexer();
164 | tagStartMarker.done(PyxlElementTypes.TAG_REFERENCE);
165 | callExpressionMarker.done(PyElementTypes.CALL_EXPRESSION);
166 | } else if (token == PyxlTokenTypes.BUILT_IN_TAG) {
167 | myBuilder.advanceLexer();
168 | } else {
169 | throw new PyxlParsingException();
170 | }
171 |
172 | if (requiredQualifiedName != null && !requiredQualifiedName.equals(fullQualifiedName)) {
173 | throw new PyxlParsingException("expected starting tag " + requiredQualifiedName);
174 | }
175 |
176 | return fullQualifiedName;
177 | }
178 |
179 | private String parseQualifiedPyxlTagName() throws PyxlParsingException {
180 | return parseQualifiedPyxlTagName(null, null, null);
181 | }
182 |
183 |
184 | /**
185 | * Attempt to parse an embedded python expression. For example:
186 | * {self.counter + 1}. If an error occurs while the embedded expression
187 | * is being parsed a parse error will be set. It is ok to call this
188 | * method whenever a embedded python expression could occur, even if
189 | * the lexer isn't currently ready to produce one.
190 | *
191 | * @return true if an embedded expression was parsed, or false
192 | * otherwise.
193 | * @throws PyxlParsingException if an error occurs while the embedded
194 | * expression is being parsed.
195 | */
196 | private boolean parsePyxlEmbed() throws PyxlParsingException {
197 | if (myBuilder.getTokenType() == PyxlTokenTypes.EMBED_START) {
198 | myBuilder.advanceLexer();
199 | parseExpression();
200 | if (myBuilder.getTokenType() == PyxlTokenTypes.EMBED_END) {
201 | myBuilder.advanceLexer();
202 | return true;
203 | } else {
204 | myBuilder.error("pyxl expected embed end");
205 | throw new PyxlParsingException();
206 | }
207 | }
208 | return false;
209 | }
210 |
211 | /**
212 | * Parse all pyxl tag attribute="value" pairs.
213 | */
214 | private boolean parsePyxlAttributes() {
215 | while (myBuilder.getTokenType() == PyxlTokenTypes.ATTRNAME) {
216 | final PsiBuilder.Marker attr = myBuilder.mark();
217 | final PsiBuilder.Marker attrName = myBuilder.mark();
218 | myBuilder.advanceLexer();
219 | attrName.done(PyxlElementTypes.ATTRNAME);
220 |
221 | if (myBuilder.getTokenType() == PyTokenTypes.EQ) {
222 | myBuilder.advanceLexer();
223 | if (parsePyxlAttributeValue()) {
224 | attr.done(PyElementTypes.KEYWORD_ARGUMENT_EXPRESSION);
225 | // Parse the next attribute="value" pair.
226 | continue;
227 | } else {
228 | // parsePyxlAttributeValue sets its own errors.
229 | attr.done(PyElementTypes.KEYWORD_ARGUMENT_EXPRESSION);
230 | return false;
231 | }
232 | } else {
233 | myBuilder.error("pyxl expected =");
234 | attr.done(PyElementTypes.KEYWORD_ARGUMENT_EXPRESSION);
235 | return false;
236 | }
237 | }
238 | return true;
239 | }
240 |
241 | /**
242 | * Parse a pyxl attribute value. If an error occurs while the attribute
243 | * value is being parsed a parse error will be set.
244 | *
245 | * @return true if a value was successfully parsed and false
246 | * otherwise.
247 | */
248 | private boolean parsePyxlAttributeValue() {
249 | IElementType token = myBuilder.getTokenType();
250 |
251 | // Consume the start of an attribute.
252 | IElementType attrStartToken = null;
253 | if (token == PyxlTokenTypes.ATTRVALUE_START) {
254 | myBuilder.advanceLexer();
255 | attrStartToken = token;
256 | }
257 |
258 | boolean foundValue = false;
259 | while (true) {
260 | token = myBuilder.getTokenType();
261 |
262 | // Attempt to parse an embed expression.
263 | try {
264 | if (parsePyxlEmbed()) {
265 | foundValue = true;
266 | continue;
267 | }
268 | } catch (PyxlParsingException e) {
269 | break;
270 | }
271 |
272 | // Or consume literal attribute values.
273 | if (token == PyxlTokenTypes.ATTRVALUE) {
274 | foundValue = true;
275 | myBuilder.advanceLexer();
276 | continue;
277 | }
278 |
279 | if (attrStartToken != null) {
280 | if (token == PyxlTokenTypes.ATTRVALUE_END) {
281 | myBuilder.advanceLexer();
282 | return true;
283 | } else {
284 | myBuilder.error("pyxl expected attribute end");
285 | return false;
286 | }
287 | } else if (foundValue) {
288 | return true;
289 | } else {
290 | myBuilder.error("pyxl expected attribute value");
291 | return false;
292 | }
293 | }
294 | return false;
295 | }
296 | }
297 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/parsing/PyxlHighlightingLexer.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.parsing;
2 |
3 | import com.intellij.psi.tree.IElementType;
4 | import com.jetbrains.python.PyNames;
5 | import com.jetbrains.python.PyTokenTypes;
6 | import com.jetbrains.python.psi.LanguageLevel;
7 |
8 | public class PyxlHighlightingLexer extends PyxlLexer {
9 | private final LanguageLevel mLanguageLevel;
10 |
11 | public PyxlHighlightingLexer(LanguageLevel languageLevel) {
12 | super();
13 | mLanguageLevel = languageLevel;
14 | }
15 |
16 | static public IElementType convertStringType(IElementType tokenType, String tokenText,
17 | LanguageLevel languageLevel, boolean unicodeImport) {
18 | if (tokenType == PyTokenTypes.SINGLE_QUOTED_STRING) {
19 | if (languageLevel.isPy3K()) {
20 | if (!tokenText.toLowerCase().startsWith("b"))
21 | return PyTokenTypes.SINGLE_QUOTED_UNICODE;
22 | } else {
23 | if ((unicodeImport && !tokenText.toLowerCase().startsWith("b"))
24 | || tokenText.toLowerCase().startsWith("u"))
25 | return PyTokenTypes.SINGLE_QUOTED_UNICODE;
26 | }
27 | }
28 | if (tokenType == PyTokenTypes.TRIPLE_QUOTED_STRING) {
29 | if (languageLevel.isPy3K()) {
30 | if (!tokenText.toLowerCase().startsWith("b"))
31 | return PyTokenTypes.TRIPLE_QUOTED_UNICODE;
32 | } else {
33 | if ((unicodeImport && !tokenText.toLowerCase().startsWith("b"))
34 | || tokenText.toLowerCase().startsWith("u"))
35 | return PyTokenTypes.TRIPLE_QUOTED_UNICODE;
36 | }
37 | }
38 | return tokenType;
39 | }
40 |
41 | public IElementType convertStringType(IElementType tokenType, String tokenText) {
42 | return convertStringType(tokenType, tokenText, mLanguageLevel, hasUnicodeImport);
43 | }
44 |
45 | @Override
46 | public IElementType getTokenType() {
47 | final IElementType tokenType = super.getTokenType();
48 |
49 | if (PyTokenTypes.STRING_NODES.contains(tokenType)) {
50 | return convertStringType(tokenType, getTokenText());
51 | }
52 |
53 | if (tokenType == PyTokenTypes.IDENTIFIER) {
54 | final String tokenText = getTokenText();
55 |
56 | if (mLanguageLevel.hasWithStatement()) {
57 | if (tokenText.equals("with")) return PyTokenTypes.WITH_KEYWORD;
58 | if (tokenText.equals("as")) return PyTokenTypes.AS_KEYWORD;
59 | }
60 |
61 | if (mLanguageLevel.hasPrintStatement()) {
62 | if (tokenText.equals("print")) return PyTokenTypes.PRINT_KEYWORD;
63 | }
64 |
65 | if (mLanguageLevel.isPy3K()) {
66 | if (tokenText.equals("None")) return PyTokenTypes.NONE_KEYWORD;
67 | if (tokenText.equals("True")) return PyTokenTypes.TRUE_KEYWORD;
68 | if (tokenText.equals("False")) return PyTokenTypes.FALSE_KEYWORD;
69 | if (tokenText.equals("nonlocal")) return PyTokenTypes.NONLOCAL_KEYWORD;
70 | if (tokenText.equals("__debug__")) return PyTokenTypes.DEBUG_KEYWORD;
71 | } else if (tokenText.equals("exec")) {
72 | return PyTokenTypes.EXEC_KEYWORD;
73 | }
74 | }
75 |
76 | return tokenType;
77 | }
78 |
79 | private enum state {
80 | init,
81 | pending_future,
82 | pending_import,
83 | pending_lpar,
84 | pending_id,
85 | pending_comma,
86 | stop
87 | }
88 |
89 | private state myState = state.init;
90 | private boolean hasUnicodeImport = false;
91 | private int myImportOffset = -1;
92 |
93 | @Override
94 | public void advance() {
95 | IElementType type = super.getTokenType();
96 | switch (myState) {
97 | case init:
98 | if (type == PyTokenTypes.BACKSLASH) break;
99 | if (PyTokenTypes.WHITESPACE_OR_LINEBREAK.contains(type)) break;
100 | if (PyTokenTypes.END_OF_LINE_COMMENT == type) break;
101 | if (PyTokenTypes.DOCSTRING == type) break;
102 | if (type == PyTokenTypes.FROM_KEYWORD)
103 | myState = state.pending_future;
104 | else myState = state.stop;
105 | break;
106 | case pending_future:
107 | if (type == PyTokenTypes.BACKSLASH) break;
108 | if (PyTokenTypes.WHITESPACE_OR_LINEBREAK.contains(type)) break;
109 | if (type == PyTokenTypes.IDENTIFIER && PyNames.FUTURE_MODULE.equals(super.getTokenText()))
110 | myState = state.pending_import;
111 | else myState = state.stop;
112 | break;
113 | case pending_import:
114 | if (type == PyTokenTypes.BACKSLASH) break;
115 | if (PyTokenTypes.WHITESPACE_OR_LINEBREAK.contains(type)) break;
116 | if (type == PyTokenTypes.IMPORT_KEYWORD)
117 | myState = state.pending_lpar;
118 | else myState = state.stop;
119 | break;
120 |
121 | case pending_lpar:
122 | if (type == PyTokenTypes.LPAR) {
123 | myState = state.pending_id;
124 | break;
125 | }
126 | case pending_id:
127 | if (type == PyTokenTypes.BACKSLASH) break;
128 | if (PyTokenTypes.WHITESPACE_OR_LINEBREAK.contains(type)) break;
129 | if (type == PyTokenTypes.IDENTIFIER) {
130 | myState = state.pending_comma;
131 | if (PyNames.UNICODE_LITERALS.equals(super.getTokenText())) {
132 | hasUnicodeImport = true;
133 | myImportOffset = getTokenEnd();
134 | }
135 | } else myState = state.init;
136 | break;
137 | case pending_comma:
138 | if (type == PyTokenTypes.RPAR) break;
139 | if (type == PyTokenTypes.BACKSLASH) break;
140 | if (PyTokenTypes.LINE_BREAK == type) myState = state.init;
141 | if (PyTokenTypes.WHITESPACE_OR_LINEBREAK.contains(type)) break;
142 | if (type == PyTokenTypes.COMMA)
143 | myState = state.pending_id;
144 | break;
145 | case stop:
146 | break;
147 | }
148 | super.advance();
149 | }
150 |
151 |
152 | public int getImportOffset() {
153 | return myImportOffset;
154 | }
155 |
156 | public void clearState(int position) {
157 | myState = state.init;
158 | myImportOffset = position;
159 | hasUnicodeImport = false;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/parsing/PyxlIndentingLexer.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.parsing;
2 |
3 | import com.intellij.psi.tree.TokenSet;
4 | import com.jetbrains.python.PyTokenTypes;
5 | import com.jetbrains.python.lexer.PythonIndentingProcessor;
6 |
7 | import java.io.Reader;
8 |
9 | public class PyxlIndentingLexer extends PythonIndentingProcessor {
10 | public PyxlIndentingLexer() {
11 | super(new _PyxlLexer((Reader) null), TokenSet.EMPTY);
12 | }
13 |
14 | boolean addFinalBreak = true;
15 |
16 | protected void processSpecialTokens() {
17 | super.processSpecialTokens();
18 | int tokenStart = getBaseTokenStart();
19 | if (getBaseTokenType() == null && addFinalBreak) {
20 | pushToken(PyTokenTypes.STATEMENT_BREAK, tokenStart, tokenStart);
21 | addFinalBreak = false;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/parsing/PyxlLexer.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.parsing;
2 |
3 | import com.intellij.lexer.FlexAdapter;
4 |
5 | import java.io.Reader;
6 |
7 | public class PyxlLexer extends FlexAdapter {
8 | public PyxlLexer() {
9 | super(new _PyxlLexer((Reader) null));
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/parsing/PyxlParser.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.parsing;
2 |
3 | import com.intellij.lang.PsiBuilder;
4 | import com.jetbrains.python.parsing.ParsingContext;
5 | import com.jetbrains.python.parsing.PyParser;
6 | import com.jetbrains.python.parsing.StatementParsing;
7 | import com.jetbrains.python.psi.LanguageLevel;
8 |
9 | class PyxlParser extends PyParser {
10 | protected ParsingContext createParsingContext(
11 | PsiBuilder builder, LanguageLevel languageLevel,
12 | StatementParsing.FUTURE futureFlag) {
13 |
14 | return new PyxlParsingContext(builder, languageLevel, futureFlag);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/parsing/PyxlParserDefinition.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.parsing;
2 |
3 | import com.christofferklang.pyxl.PyxlTokenTypes;
4 | import com.intellij.lang.PsiParser;
5 | import com.intellij.lexer.Lexer;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.psi.tree.TokenSet;
8 | import com.jetbrains.python.PythonParserDefinition;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | public class PyxlParserDefinition extends PythonParserDefinition {
12 |
13 | @NotNull
14 | @Override
15 | public Lexer createLexer(Project project) {
16 | return new PyxlIndentingLexer();
17 | }
18 |
19 | @NotNull
20 | public PsiParser createParser(Project project) {
21 | return new PyxlParser();
22 | }
23 |
24 | @NotNull
25 | @Override
26 | public TokenSet getStringLiteralElements() {
27 | return TokenSet.orSet(super.getStringLiteralElements(), TokenSet.create(PyxlTokenTypes.STRING));
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/parsing/PyxlParsingContext.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.parsing;
2 |
3 | import com.intellij.lang.PsiBuilder;
4 | import com.jetbrains.python.parsing.ExpressionParsing;
5 | import com.jetbrains.python.parsing.ParsingContext;
6 | import com.jetbrains.python.parsing.StatementParsing;
7 | import com.jetbrains.python.psi.LanguageLevel;
8 |
9 | class PyxlParsingContext extends ParsingContext {
10 | private final PyxlExpressionParsing pyxlExpressionParser;
11 |
12 | public PyxlParsingContext(
13 | final PsiBuilder builder, LanguageLevel languageLevel,
14 | StatementParsing.FUTURE futureFlag) {
15 | super(builder, languageLevel, futureFlag);
16 | pyxlExpressionParser = new PyxlExpressionParsing(this);
17 | }
18 |
19 | public ExpressionParsing getExpressionParser() {
20 | return pyxlExpressionParser;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/psi/PythonClassReference.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.psi;
2 |
3 | import com.intellij.lang.ASTNode;
4 | import com.intellij.psi.PsiElement;
5 | import com.intellij.psi.tree.TokenSet;
6 | import com.intellij.psi.util.QualifiedName;
7 | import com.jetbrains.python.psi.*;
8 | import com.jetbrains.python.psi.impl.PyReferenceExpressionImpl;
9 | import org.jetbrains.annotations.Nullable;
10 |
11 | import java.util.HashSet;
12 | import java.util.List;
13 | import java.util.Set;
14 | import java.util.WeakHashMap;
15 |
16 | public class PythonClassReference extends PyReferenceExpressionImpl {
17 | private static final Set EMPTY_HASH_SET = new HashSet();
18 | private static final Set PYXL_TAG_NAMES = new HashSet();
19 |
20 | static {
21 | // These are pulled from pyxl/html.py
22 | PYXL_TAG_NAMES.add("x_a");
23 | PYXL_TAG_NAMES.add("x_abbr");
24 | PYXL_TAG_NAMES.add("x_acronym");
25 | PYXL_TAG_NAMES.add("x_address");
26 | PYXL_TAG_NAMES.add("x_area");
27 | PYXL_TAG_NAMES.add("x_article");
28 | PYXL_TAG_NAMES.add("x_aside");
29 | PYXL_TAG_NAMES.add("x_audio");
30 | PYXL_TAG_NAMES.add("x_b");
31 | PYXL_TAG_NAMES.add("x_big");
32 | PYXL_TAG_NAMES.add("x_blockquote");
33 | PYXL_TAG_NAMES.add("x_body");
34 | PYXL_TAG_NAMES.add("x_br");
35 | PYXL_TAG_NAMES.add("x_button");
36 | PYXL_TAG_NAMES.add("x_canvas");
37 | PYXL_TAG_NAMES.add("x_caption");
38 | PYXL_TAG_NAMES.add("x_cite");
39 | PYXL_TAG_NAMES.add("x_code");
40 | PYXL_TAG_NAMES.add("x_col");
41 | PYXL_TAG_NAMES.add("x_colgroup");
42 | PYXL_TAG_NAMES.add("x_cond_comment");
43 | PYXL_TAG_NAMES.add("x_datalist");
44 | PYXL_TAG_NAMES.add("x_dd");
45 | PYXL_TAG_NAMES.add("x_del");
46 | PYXL_TAG_NAMES.add("x_dfn");
47 | PYXL_TAG_NAMES.add("x_div");
48 | PYXL_TAG_NAMES.add("x_dl");
49 | PYXL_TAG_NAMES.add("x_dt");
50 | PYXL_TAG_NAMES.add("x_em");
51 | PYXL_TAG_NAMES.add("x_embed");
52 | PYXL_TAG_NAMES.add("x_fieldset");
53 | PYXL_TAG_NAMES.add("x_figcaption");
54 | PYXL_TAG_NAMES.add("x_figure");
55 | PYXL_TAG_NAMES.add("x_footer");
56 | PYXL_TAG_NAMES.add("x_form");
57 | PYXL_TAG_NAMES.add("x_form_error");
58 | PYXL_TAG_NAMES.add("x_frag");
59 | PYXL_TAG_NAMES.add("x_frame");
60 | PYXL_TAG_NAMES.add("x_frameset");
61 | PYXL_TAG_NAMES.add("x_h1");
62 | PYXL_TAG_NAMES.add("x_h2");
63 | PYXL_TAG_NAMES.add("x_h3");
64 | PYXL_TAG_NAMES.add("x_h4");
65 | PYXL_TAG_NAMES.add("x_h5");
66 | PYXL_TAG_NAMES.add("x_h6");
67 | PYXL_TAG_NAMES.add("x_head");
68 | PYXL_TAG_NAMES.add("x_header");
69 | PYXL_TAG_NAMES.add("x_hr");
70 | PYXL_TAG_NAMES.add("x_html");
71 | PYXL_TAG_NAMES.add("x_html_comment");
72 | PYXL_TAG_NAMES.add("x_html_decl");
73 | PYXL_TAG_NAMES.add("x_html_element");
74 | PYXL_TAG_NAMES.add("x_html_element_nochild");
75 | PYXL_TAG_NAMES.add("x_html_marked_decl");
76 | PYXL_TAG_NAMES.add("x_html_ms_decl");
77 | PYXL_TAG_NAMES.add("x_i");
78 | PYXL_TAG_NAMES.add("x_iframe");
79 | PYXL_TAG_NAMES.add("x_img");
80 | PYXL_TAG_NAMES.add("x_input");
81 | PYXL_TAG_NAMES.add("x_ins");
82 | PYXL_TAG_NAMES.add("x_kbd");
83 | PYXL_TAG_NAMES.add("x_label");
84 | PYXL_TAG_NAMES.add("x_legend");
85 | PYXL_TAG_NAMES.add("x_li");
86 | PYXL_TAG_NAMES.add("x_link");
87 | PYXL_TAG_NAMES.add("x_main");
88 | PYXL_TAG_NAMES.add("x_map");
89 | PYXL_TAG_NAMES.add("x_meta");
90 | PYXL_TAG_NAMES.add("x_nav");
91 | PYXL_TAG_NAMES.add("x_noframes");
92 | PYXL_TAG_NAMES.add("x_noscript");
93 | PYXL_TAG_NAMES.add("x_object");
94 | PYXL_TAG_NAMES.add("x_ol");
95 | PYXL_TAG_NAMES.add("x_optgroup");
96 | PYXL_TAG_NAMES.add("x_option");
97 | PYXL_TAG_NAMES.add("x_p");
98 | PYXL_TAG_NAMES.add("x_param");
99 | PYXL_TAG_NAMES.add("x_pre");
100 | PYXL_TAG_NAMES.add("x_progress");
101 | PYXL_TAG_NAMES.add("x_q");
102 | PYXL_TAG_NAMES.add("x_rawhtml");
103 | PYXL_TAG_NAMES.add("x_samp");
104 | PYXL_TAG_NAMES.add("x_script");
105 | PYXL_TAG_NAMES.add("x_section");
106 | PYXL_TAG_NAMES.add("x_select");
107 | PYXL_TAG_NAMES.add("x_small");
108 | PYXL_TAG_NAMES.add("x_span");
109 | PYXL_TAG_NAMES.add("x_strong");
110 | PYXL_TAG_NAMES.add("x_style");
111 | PYXL_TAG_NAMES.add("x_sub");
112 | PYXL_TAG_NAMES.add("x_sup");
113 | PYXL_TAG_NAMES.add("x_table");
114 | PYXL_TAG_NAMES.add("x_tbody");
115 | PYXL_TAG_NAMES.add("x_td");
116 | PYXL_TAG_NAMES.add("x_textarea");
117 | PYXL_TAG_NAMES.add("x_tfoot");
118 | PYXL_TAG_NAMES.add("x_th");
119 | PYXL_TAG_NAMES.add("x_thead");
120 | PYXL_TAG_NAMES.add("x_time");
121 | PYXL_TAG_NAMES.add("x_title");
122 | PYXL_TAG_NAMES.add("x_tr");
123 | PYXL_TAG_NAMES.add("x_tt");
124 | PYXL_TAG_NAMES.add("x_u");
125 | PYXL_TAG_NAMES.add("x_ul");
126 | PYXL_TAG_NAMES.add("x_var");
127 | PYXL_TAG_NAMES.add("x_video");
128 | }
129 |
130 | private static Set mCachedSpecialPyxlTagNames = null;
131 | private static WeakHashMap sHtmlImportCache =
132 | new WeakHashMap();
133 |
134 | public PythonClassReference(ASTNode astNode) {
135 | super(astNode);
136 | }
137 |
138 | @Nullable
139 | @Override
140 | public String getReferencedName() {
141 | return pyxlClassName(getNode().getText());
142 | }
143 |
144 | @Nullable
145 | @Override
146 | public PyExpression getQualifier() {
147 | PyExpression realQualifier = super.getQualifier();
148 | if (realQualifier == null && isPyxlHtmlTag(getReferencedName())) {
149 | // Implicitly assume the tag is a reference to a pyxl html tag if the pyxl html module is imported and we
150 | // aren't using a qualifier already. This will break resolution of tags defined in a more local scope than
151 | // pyxl.html (e.g. if you make your own class x_div in a file that also imports pyxl.html).
152 | // This is consistent with how Pyxl works:
153 | // https://github.com/dropbox/pyxl/blob/daa01ca026ef3dba931d3ba56118ad8f8f6bec94/pyxl/codec/parser.py#L211
154 | PyImportElement pyxlHtmlImportElement = getImportedPyxlHtmlModuleElement();
155 | if (pyxlHtmlImportElement != null) {
156 | return pyxlHtmlImportElement.getImportReferenceExpression();
157 | }
158 | }
159 | return realQualifier;
160 | }
161 |
162 | private boolean isPyxlHtmlTag(String name) {
163 | return PYXL_TAG_NAMES.contains(name);
164 | // Uncomment the line below to get the "live" set of Pyxl tags.
165 | // return getSpecialPyxlTagsFromImportedHtmlModule().contains(name);
166 | }
167 |
168 | @SuppressWarnings("UnusedDeclaration")
169 | private Set getSpecialPyxlTagsFromImportedHtmlModule() {
170 | if (mCachedSpecialPyxlTagNames == null) {
171 | PyImportElement importPyxlHtmlElement = getImportedPyxlHtmlModuleElement();
172 | if (importPyxlHtmlElement != null) {
173 | PyFile htmlModule = (PyFile) importPyxlHtmlElement.getElementNamed("html", true);
174 |
175 | mCachedSpecialPyxlTagNames = new HashSet();
176 | //noinspection ConstantConditions
177 | for (PyClass topLevelClass : htmlModule.getTopLevelClasses()) {
178 | mCachedSpecialPyxlTagNames.add(topLevelClass.getName());
179 | }
180 |
181 | // Consider transient classes in the top level scope as well
182 | for (PyFromImportStatement importStatement : htmlModule.getFromImports()) {
183 | for(PyImportElement importElement : importStatement.getImportElements()) {
184 | final String visibleName = importElement.getVisibleName();
185 | if(visibleName != null && visibleName.startsWith("x_")) {
186 | // Just swallowing all import classes starting with
187 | // x_ isn't *technically* correct (any class can be named x_), but
188 | // definitely good enough for our purposes.
189 | mCachedSpecialPyxlTagNames.add(importElement.getVisibleName());
190 | }
191 | }
192 | }
193 | }
194 | }
195 |
196 | return mCachedSpecialPyxlTagNames == null ? EMPTY_HASH_SET : mCachedSpecialPyxlTagNames;
197 | }
198 |
199 | /**
200 | * Try and a find an import statement such as "from mymodule.pyxl import html"
201 | */
202 | private PyImportElement getImportedPyxlHtmlModuleElement() {
203 | if (!(getContainingFile() instanceof PyFile)) return null; // not a python file
204 |
205 | final PyFile pyFile = (PyFile) getContainingFile();
206 | final String cacheKey = String.format("%s:%s",
207 | pyFile.getContainingDirectory(),
208 | pyFile.getName());
209 |
210 | if(sHtmlImportCache.containsKey(cacheKey)) {
211 | PyImportElement importElement = sHtmlImportCache.get(cacheKey);
212 | if(importElement != null) {
213 | return importElement;
214 | }
215 | }
216 |
217 | List imports = pyFile.getFromImports();
218 |
219 | for (PyFromImportStatement importStatement : imports) {
220 | // check for import statements that import from a "pyxl" package
221 | final QualifiedName qualifiedName = importStatement.getImportSourceQName();
222 | if (qualifiedName != null && "pyxl".equals(qualifiedName.getLastComponent())) {
223 | // check only for imports of the module "html"
224 | PyImportElement[] importedElements = importStatement.getImportElements();
225 | for (PyImportElement importedElement : importedElements) {
226 | PsiElement htmlElement = importedElement.getElementNamed("html", true);
227 | if (htmlElement instanceof PyFile) {
228 | sHtmlImportCache.put(cacheKey, importedElement);
229 | return importedElement;
230 | }
231 | }
232 | }
233 | }
234 |
235 | return null;
236 | }
237 |
238 | private String pyxlClassName(String tagName) {
239 | if (tagName.indexOf(".") > 0) {
240 | // tag contains a module reference like:
241 | final StringBuilder qualifiedTagName = new StringBuilder(tagName);
242 | final int offset = qualifiedTagName.lastIndexOf(".");
243 | tagName = qualifiedTagName.subSequence(offset + 1, qualifiedTagName.length()).toString();
244 | return "x_" + tagName;
245 | }
246 | return "x_" + tagName;
247 | }
248 |
249 | @Override
250 | public String toString() {
251 | return "PyClassTagReference: " + getReferencedName();
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/psi/PyxlArgumentList.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.psi;
2 |
3 | import com.intellij.lang.ASTNode;
4 | import com.jetbrains.python.psi.impl.PyArgumentListImpl;
5 |
6 | public class PyxlArgumentList extends PyArgumentListImpl {
7 | public PyxlArgumentList(ASTNode astNode) {
8 | super(astNode);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/psi/PyxlAttrName.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.psi;
2 |
3 | import com.intellij.lang.ASTNode;
4 | import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl;
5 |
6 | public class PyxlAttrName extends PyStringLiteralExpressionImpl {
7 | public PyxlAttrName(ASTNode astNode) {
8 | super(astNode);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/com/christofferklang/pyxl/psi/PyxlTag.java:
--------------------------------------------------------------------------------
1 | package com.christofferklang.pyxl.psi;
2 |
3 | import com.christofferklang.pyxl.PyxlElementTypes;
4 | import com.intellij.lang.ASTNode;
5 | import com.jetbrains.python.psi.PyArgumentList;
6 | import com.jetbrains.python.psi.PyExpression;
7 | import com.jetbrains.python.psi.impl.PyCallExpressionImpl;
8 | import org.jetbrains.annotations.Nullable;
9 |
10 | public class PyxlTag extends PyCallExpressionImpl {
11 | private String pyxlTagName;
12 |
13 | public PyxlTag(ASTNode astNode) {
14 | super(astNode);
15 | }
16 |
17 | @Override
18 | public String toString() {
19 | PyExpression callee = getCallee();
20 | return String.format("Pyxl Tag: %s", callee == null ? "null" : callee.getName());
21 | }
22 |
23 | @Nullable
24 | @Override
25 | public PyExpression getCallee() {
26 | return (PyExpression) findChildByType(PyxlElementTypes.TAG_REFERENCE);
27 | }
28 |
29 | @Override
30 | public PyArgumentList getArgumentList() {
31 | return (PyArgumentList) findChildByType(PyxlElementTypes.ARGUMENT_LIST);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/testdata/Attributes.py:
--------------------------------------------------------------------------------
1 | # coding: pyxl
2 |
3 |
4 |
5 |
6 | "}" />
7 |
8 |
--------------------------------------------------------------------------------
/testdata/Attributes.txt:
--------------------------------------------------------------------------------
1 | PyFile:Attributes.py(0,255)
2 | PsiComment(Py:END_OF_LINE_COMMENT)('# coding: pyxl')(0,14)
3 | PsiWhiteSpace('\n')(14,15)
4 | PsiElement(Py:LT)('<')(15,16)
5 | PsiErrorElement:Statement expected, found Py:LT(16,16)
6 |
7 | PyExpressionStatement(16,19)
8 | PyReferenceExpression: div(16,19)
9 | PsiElement(Py:IDENTIFIER)('div')(16,19)
10 | PsiErrorElement:End of statement expected(19,19)
11 |
12 | PsiWhiteSpace(' ')(19,20)
13 | PyAssignmentStatement(20,40)
14 | PyTargetExpression: attribute(20,29)
15 | PsiElement(Py:IDENTIFIER)('attribute')(20,29)
16 | PsiElement(Py:EQ)('=')(29,30)
17 | PyBinaryExpression(30,40)
18 | PyBinaryExpression(30,39)
19 | PyStringLiteralExpression: value(30,37)
20 | PsiElement(Py:SINGLE_QUOTED_STRING)('"value"')(30,37)
21 | PsiWhiteSpace(' ')(37,38)
22 | PsiElement(Py:DIV)('/')(38,39)
23 | PsiErrorElement:expression expected(39,39)
24 |
25 | PsiElement(Py:GT)('>')(39,40)
26 | PsiErrorElement:expression expected(40,40)
27 |
28 | PsiWhiteSpace('\n')(40,41)
29 | PyExpressionStatement(41,81)
30 | Pyxl Tag: null(41,81)
31 | PsiElement(Py:PYXL TAGBEGIN <)('<')(41,42)
32 | PyCallExpression: div(42,45)
33 | PyClassTagReference: x_div(42,45)
34 | PsiElement(Py:PYXL TAGNAME)('div')(42,45)
35 | PsiWhiteSpace(' ')(45,46)
36 | PyKeywordArgumentImpl: null(46,78)
37 | PyxlAttrName: (46,55)
38 | PsiElement(Py:PYXL ATTRNAME)('attribute')(46,55)
39 | PsiElement(Py:EQ)('=')(55,56)
40 | PsiElement(Py:PYXL PYTHON EMBED BEGIN {)('{')(56,57)
41 | PyStringLiteralExpression: no wrapping quotes(57,77)
42 | PsiElement(Py:SINGLE_QUOTED_STRING)('"no wrapping quotes"')(57,77)
43 | PsiElement(Py:PYXL PYTHON EMBED END })('}')(77,78)
44 | PsiWhiteSpace(' ')(78,79)
45 | PyxlArgumentList(79,79)
46 |
47 | PsiElement(Py:PYXL TAGENDANDCLOSE />)('/>')(79,81)
48 | PsiWhiteSpace('\n')(81,82)
49 | PsiElement(Py:LT)('<')(82,83)
50 | PsiErrorElement:Statement expected, found Py:LT(83,83)
51 |
52 | PyExpressionStatement(83,86)
53 | PyReferenceExpression: div(83,86)
54 | PsiElement(Py:IDENTIFIER)('div')(83,86)
55 | PsiErrorElement:End of statement expected(86,86)
56 |
57 | PsiWhiteSpace(' ')(86,87)
58 | PyAssignmentStatement(87,128)
59 | PyTargetExpression: attribute(87,96)
60 | PsiElement(Py:IDENTIFIER)('attribute')(87,96)
61 | PsiElement(Py:EQ)('=')(96,97)
62 | PyBinaryExpression(97,128)
63 | PyBinaryExpression(97,127)
64 | PyStringLiteralExpression: {"wrapping single quotes"}(97,125)
65 | PsiElement(Py:SINGLE_QUOTED_STRING)(''{"wrapping single quotes"}'')(97,125)
66 | PsiWhiteSpace(' ')(125,126)
67 | PsiElement(Py:DIV)('/')(126,127)
68 | PsiErrorElement:expression expected(127,127)
69 |
70 | PsiElement(Py:GT)('>')(127,128)
71 | PsiErrorElement:expression expected(128,128)
72 |
73 | PsiWhiteSpace('\n')(128,129)
74 | PyExpressionStatement(129,175)
75 | Pyxl Tag: null(129,175)
76 | PsiElement(Py:PYXL TAGBEGIN <)('<')(129,130)
77 | PyCallExpression: div(130,133)
78 | PyClassTagReference: x_div(130,133)
79 | PsiElement(Py:PYXL TAGNAME)('div')(130,133)
80 | PsiWhiteSpace(' ')(133,134)
81 | PyKeywordArgumentImpl: null(134,172)
82 | PyxlAttrName: (134,143)
83 | PsiElement(Py:PYXL ATTRNAME)('attribute')(134,143)
84 | PsiElement(Py:EQ)('=')(143,144)
85 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(144,145)
86 | PsiElement(Py:PYXL PYTHON EMBED BEGIN {)('{')(145,146)
87 | PyStringLiteralExpression: wrapping double quotes(146,170)
88 | PsiElement(Py:SINGLE_QUOTED_STRING)('"wrapping double quotes"')(146,170)
89 | PsiElement(Py:PYXL PYTHON EMBED END })('}')(170,171)
90 | PsiElement(Py:PYXL ATTRVALUE END)('"')(171,172)
91 | PsiWhiteSpace(' ')(172,173)
92 | PyxlArgumentList(173,173)
93 |
94 | PsiElement(Py:PYXL TAGENDANDCLOSE />)('/>')(173,175)
95 | PsiWhiteSpace('\n')(175,176)
96 | PsiElement(Py:LT)('<')(176,177)
97 | PsiErrorElement:Statement expected, found Py:LT(177,177)
98 |
99 | PyExpressionStatement(177,180)
100 | PyReferenceExpression: div(177,180)
101 | PsiElement(Py:IDENTIFIER)('div')(177,180)
102 | PsiErrorElement:End of statement expected(180,180)
103 |
104 | PsiWhiteSpace(' ')(180,181)
105 | PyAssignmentStatement(181,199)
106 | PyTargetExpression: attribute(181,190)
107 | PsiElement(Py:IDENTIFIER)('attribute')(181,190)
108 | PsiElement(Py:EQ)('=')(190,191)
109 | PyBinaryExpression(191,199)
110 | PyStringLiteralExpression: {(191,194)
111 | PsiElement(Py:SINGLE_QUOTED_STRING)('"{"')(191,194)
112 | PsiElement(Py:LT)('<')(194,195)
113 | PyReferenceExpression: fake(195,199)
114 | PsiElement(Py:IDENTIFIER)('fake')(195,199)
115 | PsiErrorElement:End of statement expected(199,199)
116 |
117 | PsiWhiteSpace(' ')(199,200)
118 | PyExpressionStatement(200,205)
119 | PyReferenceExpression: inner(200,205)
120 | PsiElement(Py:IDENTIFIER)('inner')(200,205)
121 | PsiErrorElement:End of statement expected(205,205)
122 |
123 | PsiWhiteSpace(' ')(205,206)
124 | PyExpressionStatement(206,219)
125 | PyBinaryExpression(206,219)
126 | PyBinaryExpression(206,218)
127 | PyBinaryExpression(206,212)
128 | PyReferenceExpression: pyxl(206,210)
129 | PsiElement(Py:IDENTIFIER)('pyxl')(206,210)
130 | PsiWhiteSpace(' ')(210,211)
131 | PsiElement(Py:DIV)('/')(211,212)
132 | PsiErrorElement:expression expected(212,212)
133 |
134 | PsiElement(Py:GT)('>')(212,213)
135 | PyBinaryExpression(213,218)
136 | PyStringLiteralExpression: }(213,216)
137 | PsiElement(Py:SINGLE_QUOTED_STRING)('"}"')(213,216)
138 | PsiWhiteSpace(' ')(216,217)
139 | PsiElement(Py:DIV)('/')(217,218)
140 | PsiErrorElement:expression expected(218,218)
141 |
142 | PsiElement(Py:GT)('>')(218,219)
143 | PsiErrorElement:expression expected(219,219)
144 |
145 | PsiWhiteSpace('\n')(219,220)
146 | PyExpressionStatement(220,255)
147 | Pyxl Tag: null(220,255)
148 | PsiElement(Py:PYXL TAGBEGIN <)('<')(220,221)
149 | PyCallExpression: div(221,224)
150 | PyClassTagReference: x_div(221,224)
151 | PsiElement(Py:PYXL TAGNAME)('div')(221,224)
152 | PsiWhiteSpace(' ')(224,225)
153 | PyKeywordArgumentImpl: null(225,252)
154 | PyxlAttrName: (225,244)
155 | PsiElement(Py:PYXL ATTRNAME)('namspaced:attribute')(225,244)
156 | PsiElement(Py:EQ)('=')(244,245)
157 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(245,246)
158 | PsiElement(Py:PYXL ATTRVALUE)('value')(246,251)
159 | PsiElement(Py:PYXL ATTRVALUE END)('"')(251,252)
160 | PsiWhiteSpace(' ')(252,253)
161 | PyxlArgumentList(253,253)
162 |
163 | PsiElement(Py:PYXL TAGENDANDCLOSE />)('/>')(253,255)
--------------------------------------------------------------------------------
/testdata/Comments.py:
--------------------------------------------------------------------------------
1 | # coding: pyxl
2 |
3 | def stuff_with_comments():
4 | return (
5 |
6 |
7 |
9 | ]]>
10 |
11 |
12 |
13 | )
14 |
--------------------------------------------------------------------------------
/testdata/Comments.txt:
--------------------------------------------------------------------------------
1 | PyFile:Comments.py(0,249)
2 | PsiComment(Py:END_OF_LINE_COMMENT)('# coding: pyxl')(0,14)
3 | PsiWhiteSpace('\n\n')(14,16)
4 | PyFunction('stuff_with_comments')(16,249)
5 | PsiElement(Py:DEF_KEYWORD)('def')(16,19)
6 | PsiWhiteSpace(' ')(19,20)
7 | PsiElement(Py:IDENTIFIER)('stuff_with_comments')(20,39)
8 | PyParameterList(39,41)
9 | PsiElement(Py:LPAR)('(')(39,40)
10 | PsiElement(Py:RPAR)(')')(40,41)
11 | PsiElement(Py:COLON)(':')(41,42)
12 | PsiWhiteSpace('\n ')(42,47)
13 | PyStatementList(47,249)
14 | PyReturnStatement(47,249)
15 | PsiElement(Py:RETURN_KEYWORD)('return')(47,53)
16 | PsiWhiteSpace(' ')(53,54)
17 | PyParenthesizedExpression(54,249)
18 | PsiElement(Py:LPAR)('(')(54,55)
19 | PsiWhiteSpace('\n ')(55,62)
20 | Pyxl Tag: null(62,243)
21 | PsiElement(Py:PYXL TAGBEGIN <)('<')(62,63)
22 | PyCallExpression: frag(63,67)
23 | PyClassTagReference: x_frag(63,67)
24 | PsiElement(Py:PYXL TAGNAME)('frag')(63,67)
25 | PsiElement(Py:PYXL TAGEND >)('>')(67,68)
26 | PyArgumentList(68,236)
27 | PyStringLiteralExpression: (68,77)
28 | PsiElement(Py:PYXL STRING)('\n ')(68,77)
29 | PsiComment(Py:END_OF_LINE_COMMENT)('')(77,92)
30 | PyStringLiteralExpression: (92,101)
31 | PsiElement(Py:PYXL STRING)('\n ')(92,101)
32 | Pyxl Tag: null(101,229)
33 | PsiElement(Py:PYXL TAGBEGIN <)('<')(101,102)
34 | PyCallExpression: html(102,106)
35 | PyClassTagReference: x_html(102,106)
36 | PsiElement(Py:PYXL TAGNAME)('html')(102,106)
37 | PsiElement(Py:PYXL TAGEND >)('>')(106,107)
38 | PsiComment(Py:END_OF_LINE_COMMENT)('\n ]]>')(107,188)
39 | PyArgumentList(188,222)
40 | PyStringLiteralExpression: (188,197)
41 | PsiElement(Py:PYXL STRING)('\n ')(188,197)
42 | PsiComment(Py:END_OF_LINE_COMMENT)('')(197,213)
43 | PyStringLiteralExpression: (213,222)
44 | PsiElement(Py:PYXL STRING)('\n ')(213,222)
45 | PsiElement(Py:PYXL TAGCLOSE )('')(222,224)
46 | PyCallExpression: html(224,228)
47 | PyClassTagReference: x_html(224,228)
48 | PsiElement(Py:PYXL TAGNAME)('html')(224,228)
49 | PsiElement(Py:PYXL TAGEND >)('>')(228,229)
50 | PyStringLiteralExpression: (229,236)
51 | PsiElement(Py:PYXL STRING)('\n ')(229,236)
52 | PsiElement(Py:PYXL TAGCLOSE )('')(236,238)
53 | PyCallExpression: frag(238,242)
54 | PyClassTagReference: x_frag(238,242)
55 | PsiElement(Py:PYXL TAGNAME)('frag')(238,242)
56 | PsiElement(Py:PYXL TAGEND >)('>')(242,243)
57 | PsiWhiteSpace('\n ')(243,248)
58 | PsiElement(Py:RPAR)(')')(248,249)
--------------------------------------------------------------------------------
/testdata/ParsingTestData.py:
--------------------------------------------------------------------------------
1 | # coding: pyxl
2 |
3 | def x_hi():
4 | pass
5 |
6 | def foo():
7 | yy = 20
8 | scaley = (yy<5) or (yy>100)
9 |
10 | return (
11 |
12 |
13 | goo
14 |
15 |
16 | zoo
17 |
18 |
19 |
20 |
21 | )
22 |
23 | def unparenthesized_multiline_expression():
24 | return
28 |
29 | def zoo():
30 | str = "abcdefg"
31 | return(
32 |
40 | )
41 |
42 | return (
43 | One
44 | )
45 |
46 | def goo():
47 | b = True
48 | return (
55 | {5+5}
56 |
57 | )
58 |
--------------------------------------------------------------------------------
/testdata/ParsingTestData.txt:
--------------------------------------------------------------------------------
1 | PyFile:ParsingTestData.py(0,978)
2 | PsiComment(Py:END_OF_LINE_COMMENT)('# coding: pyxl')(0,14)
3 | PsiWhiteSpace('\n\n')(14,16)
4 | PyFunction('x_hi')(16,36)
5 | PsiElement(Py:DEF_KEYWORD)('def')(16,19)
6 | PsiWhiteSpace(' ')(19,20)
7 | PsiElement(Py:IDENTIFIER)('x_hi')(20,24)
8 | PyParameterList(24,26)
9 | PsiElement(Py:LPAR)('(')(24,25)
10 | PsiElement(Py:RPAR)(')')(25,26)
11 | PsiElement(Py:COLON)(':')(26,27)
12 | PsiWhiteSpace('\n ')(27,32)
13 | PyStatementList(32,36)
14 | PyPassStatement(32,36)
15 | PsiElement(Py:PASS_KEYWORD)('pass')(32,36)
16 | PsiWhiteSpace('\n\n')(36,38)
17 | PyFunction('foo')(38,334)
18 | PsiElement(Py:DEF_KEYWORD)('def')(38,41)
19 | PsiWhiteSpace(' ')(41,42)
20 | PsiElement(Py:IDENTIFIER)('foo')(42,45)
21 | PyParameterList(45,47)
22 | PsiElement(Py:LPAR)('(')(45,46)
23 | PsiElement(Py:RPAR)(')')(46,47)
24 | PsiElement(Py:COLON)(':')(47,48)
25 | PsiWhiteSpace('\n ')(48,53)
26 | PyStatementList(53,334)
27 | PyAssignmentStatement(53,60)
28 | PyTargetExpression: yy(53,55)
29 | PsiElement(Py:IDENTIFIER)('yy')(53,55)
30 | PsiWhiteSpace(' ')(55,56)
31 | PsiElement(Py:EQ)('=')(56,57)
32 | PsiWhiteSpace(' ')(57,58)
33 | PyNumericLiteralExpression(58,60)
34 | PsiElement(Py:INTEGER_LITERAL)('20')(58,60)
35 | PsiWhiteSpace('\n ')(60,65)
36 | PyAssignmentStatement(65,92)
37 | PyTargetExpression: scaley(65,71)
38 | PsiElement(Py:IDENTIFIER)('scaley')(65,71)
39 | PsiWhiteSpace(' ')(71,72)
40 | PsiElement(Py:EQ)('=')(72,73)
41 | PsiWhiteSpace(' ')(73,74)
42 | PyBinaryExpression(74,92)
43 | PyParenthesizedExpression(74,80)
44 | PsiElement(Py:LPAR)('(')(74,75)
45 | PyBinaryExpression(75,79)
46 | PyReferenceExpression: yy(75,77)
47 | PsiElement(Py:IDENTIFIER)('yy')(75,77)
48 | PsiElement(Py:LT)('<')(77,78)
49 | PyNumericLiteralExpression(78,79)
50 | PsiElement(Py:INTEGER_LITERAL)('5')(78,79)
51 | PsiElement(Py:RPAR)(')')(79,80)
52 | PsiWhiteSpace(' ')(80,81)
53 | PsiElement(Py:OR_KEYWORD)('or')(81,83)
54 | PsiWhiteSpace(' ')(83,84)
55 | PyParenthesizedExpression(84,92)
56 | PsiElement(Py:LPAR)('(')(84,85)
57 | PyBinaryExpression(85,91)
58 | PyReferenceExpression: yy(85,87)
59 | PsiElement(Py:IDENTIFIER)('yy')(85,87)
60 | PsiElement(Py:GT)('>')(87,88)
61 | PyNumericLiteralExpression(88,91)
62 | PsiElement(Py:INTEGER_LITERAL)('100')(88,91)
63 | PsiElement(Py:RPAR)(')')(91,92)
64 | PsiWhiteSpace('\n\n ')(92,98)
65 | PyReturnStatement(98,334)
66 | PsiElement(Py:RETURN_KEYWORD)('return')(98,104)
67 | PsiWhiteSpace(' ')(104,105)
68 | PyParenthesizedExpression(105,334)
69 | PsiElement(Py:LPAR)('(')(105,106)
70 | PsiWhiteSpace('\n ')(106,115)
71 | Pyxl Tag: null(115,328)
72 | PsiElement(Py:PYXL TAGBEGIN <)('<')(115,116)
73 | PyCallExpression: hi(116,118)
74 | PyClassTagReference: x_hi(116,118)
75 | PsiElement(Py:PYXL TAGNAME)('hi')(116,118)
76 | PsiElement(Py:PYXL TAGEND >)('>')(118,119)
77 | PyArgumentList(119,323)
78 | PyStringLiteralExpression: (119,131)
79 | PsiElement(Py:PYXL STRING)('\n ')(119,131)
80 | Pyxl Tag: null(131,184)
81 | PsiElement(Py:PYXL TAGBEGIN <)('<')(131,132)
82 | PsiElement(Py:PYXL BUILT IN TAG)('if')(132,134)
83 | PsiWhiteSpace(' ')(134,135)
84 | PyKeywordArgumentImpl: null(135,145)
85 | PyxlAttrName: (135,139)
86 | PsiElement(Py:PYXL ATTRNAME)('cond')(135,139)
87 | PsiElement(Py:EQ)('=')(139,140)
88 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(140,141)
89 | PsiElement(Py:PYXL ATTRVALUE)('moo')(141,144)
90 | PsiElement(Py:PYXL ATTRVALUE END)('"')(144,145)
91 | PsiElement(Py:PYXL TAGEND >)('>')(145,146)
92 | PyArgumentList(146,179)
93 | PyStringLiteralExpression: (146,179)
94 | PsiElement(Py:PYXL STRING)('\n goo\n ')(146,179)
95 | PsiElement(Py:PYXL TAGCLOSE )('')(179,181)
96 | PsiElement(Py:PYXL BUILT IN TAG)('if')(181,183)
97 | PsiElement(Py:PYXL TAGEND >)('>')(183,184)
98 | PyStringLiteralExpression: (184,197)
99 | PsiElement(Py:PYXL STRING)('\n ')(184,197)
100 | Pyxl Tag: null(197,243)
101 | PsiElement(Py:PYXL TAGBEGIN <)('<')(197,198)
102 | PsiElement(Py:PYXL BUILT IN TAG)('else')(198,202)
103 | PsiElement(Py:PYXL TAGEND >)('>')(202,203)
104 | PyArgumentList(203,236)
105 | PyStringLiteralExpression: (203,236)
106 | PsiElement(Py:PYXL STRING)('\n zoo\n ')(203,236)
107 | PsiElement(Py:PYXL TAGCLOSE )('')(236,238)
108 | PsiElement(Py:PYXL BUILT IN TAG)('else')(238,242)
109 | PsiElement(Py:PYXL TAGEND >)('>')(242,243)
110 | PyStringLiteralExpression: (243,256)
111 | PsiElement(Py:PYXL STRING)('\n ')(243,256)
112 | Pyxl Tag: null(256,314)
113 | PsiElement(Py:PYXL TAGBEGIN <)('<')(256,257)
114 | PyCallExpression: hi(257,259)
115 | PyClassTagReference: x_hi(257,259)
116 | PsiElement(Py:PYXL TAGNAME)('hi')(257,259)
117 | PsiWhiteSpace(' ')(259,260)
118 | PyKeywordArgumentImpl: null(260,267)
119 | PyxlAttrName: (260,263)
120 | PsiElement(Py:PYXL ATTRNAME)('boo')(260,263)
121 | PsiElement(Py:EQ)('=')(263,264)
122 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(264,265)
123 | PsiElement(Py:PYXL ATTRVALUE)('#')(265,266)
124 | PsiElement(Py:PYXL ATTRVALUE END)('"')(266,267)
125 | PsiWhiteSpace(' ')(267,268)
126 | PyKeywordArgumentImpl: null(268,295)
127 | PyxlAttrName: (268,270)
128 | PsiElement(Py:PYXL ATTRNAME)('id')(268,270)
129 | PsiElement(Py:EQ)('=')(270,271)
130 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(271,272)
131 | PsiElement(Py:PYXL ATTRVALUE)('Zasdf ')(272,278)
132 | PsiElement(Py:PYXL PYTHON EMBED BEGIN {)('{')(278,279)
133 | PsiWhiteSpace(' ')(279,280)
134 | PyStringLiteralExpression: (280,283)
135 | PsiElement(Py:SINGLE_QUOTED_STRING)('" "')(280,283)
136 | PsiWhiteSpace(' ')(283,284)
137 | PsiElement(Py:PYXL PYTHON EMBED END })('}')(284,285)
138 | PsiElement(Py:PYXL ATTRVALUE)(' asdfjkl ')(285,294)
139 | PsiElement(Py:PYXL ATTRVALUE END)('"')(294,295)
140 | PsiElement(Py:PYXL TAGEND >)('>')(295,296)
141 | PyArgumentList(296,309)
142 | PyStringLiteralExpression: (296,309)
143 | PsiElement(Py:PYXL STRING)('\n ')(296,309)
144 | PsiElement(Py:PYXL TAGCLOSE )('')(309,311)
145 | PyCallExpression: hi(311,313)
146 | PyClassTagReference: x_hi(311,313)
147 | PsiElement(Py:PYXL TAGNAME)('hi')(311,313)
148 | PsiElement(Py:PYXL TAGEND >)('>')(313,314)
149 | PyStringLiteralExpression: (314,323)
150 | PsiElement(Py:PYXL STRING)('\n ')(314,323)
151 | PsiElement(Py:PYXL TAGCLOSE )('')(323,325)
152 | PyCallExpression: hi(325,327)
153 | PyClassTagReference: x_hi(325,327)
154 | PsiElement(Py:PYXL TAGNAME)('hi')(325,327)
155 | PsiElement(Py:PYXL TAGEND >)('>')(327,328)
156 | PsiWhiteSpace('\n ')(328,333)
157 | PsiElement(Py:RPAR)(')')(333,334)
158 | PsiWhiteSpace('\n\n')(334,336)
159 | PyFunction('unparenthesized_multiline_expression')(336,978)
160 | PsiElement(Py:DEF_KEYWORD)('def')(336,339)
161 | PsiWhiteSpace(' ')(339,340)
162 | PsiElement(Py:IDENTIFIER)('unparenthesized_multiline_expression')(340,376)
163 | PyParameterList(376,378)
164 | PsiElement(Py:LPAR)('(')(376,377)
165 | PsiElement(Py:RPAR)(')')(377,378)
166 | PsiElement(Py:COLON)(':')(378,379)
167 | PsiWhiteSpace('\n ')(379,384)
168 | PyStatementList(384,978)
169 | PyReturnStatement(384,394)
170 | PsiElement(Py:RETURN_KEYWORD)('return')(384,390)
171 | PsiWhiteSpace(' ')(390,391)
172 | Pyxl Tag: null(391,394)
173 | PsiElement(Py:PYXL TAGBEGIN <)('<')(391,392)
174 | PyCallExpression: hi(392,394)
175 | PyClassTagReference: x_hi(392,394)
176 | PsiElement(Py:PYXL TAGNAME)('hi')(392,394)
177 | PsiWhiteSpace('\n ')(394,403)
178 | PsiErrorElement:Unexpected indent(403,403)
179 |
180 | PsiElement(Py:PYXL ATTRNAME)('id')(403,405)
181 | PsiErrorElement:Statement expected, found Py:PYXL ATTRNAME(405,405)
182 |
183 | PsiElement(Py:EQ)('=')(405,406)
184 | PsiErrorElement:Statement expected, found Py:EQ(406,406)
185 |
186 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(406,407)
187 | PsiErrorElement:Statement expected, found Py:PYXL ATTRVALUE BEGIN(407,407)
188 |
189 | PsiElement(Py:PYXL ATTRVALUE)('foo')(407,410)
190 | PsiErrorElement:Statement expected, found Py:PYXL ATTRVALUE(410,410)
191 |
192 | PsiElement(Py:PYXL ATTRVALUE END)('"')(410,411)
193 | PsiErrorElement:Statement expected, found Py:PYXL ATTRVALUE END(411,411)
194 |
195 | PsiWhiteSpace('\n ')(411,420)
196 | PsiElement(Py:PYXL ATTRNAME)('class')(420,425)
197 | PsiErrorElement:Statement expected, found Py:PYXL ATTRNAME(425,425)
198 |
199 | PsiElement(Py:EQ)('=')(425,426)
200 | PsiErrorElement:Statement expected, found Py:EQ(426,426)
201 |
202 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(426,427)
203 | PsiErrorElement:Statement expected, found Py:PYXL ATTRVALUE BEGIN(427,427)
204 |
205 | PsiElement(Py:PYXL ATTRVALUE)('bar')(427,430)
206 | PsiErrorElement:Statement expected, found Py:PYXL ATTRVALUE(430,430)
207 |
208 | PsiElement(Py:PYXL ATTRVALUE END)('"')(430,431)
209 | PsiErrorElement:Statement expected, found Py:PYXL ATTRVALUE END(431,431)
210 |
211 | PsiErrorElement:Statement expected, found Py:DEDENT(431,431)
212 |
213 | PsiWhiteSpace('\n ')(431,436)
214 | PsiElement(Py:PYXL TAGEND >)('>')(436,437)
215 | PsiErrorElement:Statement expected, found Py:PYXL TAGEND >(437,437)
216 |
217 | PsiElement(Py:PYXL TAGCLOSE )('')(437,439)
218 | PsiErrorElement:Statement expected, found Py:PYXL TAGCLOSE (439,439)
219 |
220 | PsiElement(Py:PYXL TAGNAME)('hi')(439,441)
221 | PsiErrorElement:Statement expected, found Py:PYXL TAGNAME(441,441)
222 |
223 | PsiElement(Py:PYXL TAGEND >)('>')(441,442)
224 | PsiErrorElement:Statement expected, found Py:PYXL TAGEND >(442,442)
225 |
226 | PsiErrorElement:Statement expected, found Py:DEDENT(442,442)
227 |
228 | PsiWhiteSpace('\n\n')(442,444)
229 | PyFunction('zoo')(444,714)
230 | PsiElement(Py:DEF_KEYWORD)('def')(444,447)
231 | PsiWhiteSpace(' ')(447,448)
232 | PsiElement(Py:IDENTIFIER)('zoo')(448,451)
233 | PyParameterList(451,453)
234 | PsiElement(Py:LPAR)('(')(451,452)
235 | PsiElement(Py:RPAR)(')')(452,453)
236 | PsiElement(Py:COLON)(':')(453,454)
237 | PsiWhiteSpace('\n ')(454,459)
238 | PyStatementList(459,714)
239 | PyAssignmentStatement(459,474)
240 | PyTargetExpression: str(459,462)
241 | PsiElement(Py:IDENTIFIER)('str')(459,462)
242 | PsiWhiteSpace(' ')(462,463)
243 | PsiElement(Py:EQ)('=')(463,464)
244 | PsiWhiteSpace(' ')(464,465)
245 | PyStringLiteralExpression: abcdefg(465,474)
246 | PsiElement(Py:SINGLE_QUOTED_STRING)('"abcdefg"')(465,474)
247 | PsiWhiteSpace('\n ')(474,479)
248 | PyReturnStatement(479,638)
249 | PsiElement(Py:RETURN_KEYWORD)('return')(479,485)
250 | PyParenthesizedExpression(485,638)
251 | PsiElement(Py:LPAR)('(')(485,486)
252 | PsiWhiteSpace('\n ')(486,495)
253 | Pyxl Tag: null(495,632)
254 | PsiElement(Py:PYXL TAGBEGIN <)('<')(495,496)
255 | PyCallExpression: hi(496,498)
256 | PyClassTagReference: x_hi(496,498)
257 | PsiElement(Py:PYXL TAGNAME)('hi')(496,498)
258 | PsiWhiteSpace(' ')(498,499)
259 | PyKeywordArgumentImpl: null(499,514)
260 | PyxlAttrName: (499,501)
261 | PsiElement(Py:PYXL ATTRNAME)('id')(499,501)
262 | PsiElement(Py:EQ)('=')(501,502)
263 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(502,503)
264 | PsiElement(Py:PYXL ATTRVALUE)('foo-footer')(503,513)
265 | PsiElement(Py:PYXL ATTRVALUE END)('"')(513,514)
266 | PsiWhiteSpace(' ')(514,515)
267 | PyKeywordArgumentImpl: null(515,539)
268 | PyxlAttrName: (515,520)
269 | PsiElement(Py:PYXL ATTRNAME)('class')(515,520)
270 | PsiElement(Py:EQ)('=')(520,521)
271 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(521,522)
272 | PsiElement(Py:PYXL ATTRVALUE)('zoo-bar clearfix')(522,538)
273 | PsiElement(Py:PYXL ATTRVALUE END)('"')(538,539)
274 | PsiElement(Py:PYXL TAGEND >)('>')(539,540)
275 | PyArgumentList(540,627)
276 | PyStringLiteralExpression: (540,554)
277 | PsiElement(Py:PYXL STRING)('\n\n ')(540,554)
278 | Pyxl Tag: null(554,598)
279 | PsiElement(Py:PYXL TAGBEGIN <)('<')(554,555)
280 | PyCallExpression: hi(555,557)
281 | PyClassTagReference: x_hi(555,557)
282 | PsiElement(Py:PYXL TAGNAME)('hi')(555,557)
283 | PsiElement(Py:PYXL TAGEND >)('>')(557,558)
284 | PyArgumentList(558,593)
285 | PyStringLiteralExpression: (558,575)
286 | PsiElement(Py:PYXL STRING)('\n ')(558,575)
287 | PsiElement(Py:PYXL PYTHON EMBED BEGIN {)('{')(575,576)
288 | PyReferenceExpression: str(576,579)
289 | PsiElement(Py:IDENTIFIER)('str')(576,579)
290 | PsiElement(Py:PYXL PYTHON EMBED END })('}')(579,580)
291 | PyStringLiteralExpression: (580,593)
292 | PsiElement(Py:PYXL STRING)('\n ')(580,593)
293 | PsiElement(Py:PYXL TAGCLOSE )('')(593,595)
294 | PyCallExpression: hi(595,597)
295 | PyClassTagReference: x_hi(595,597)
296 | PsiElement(Py:PYXL TAGNAME)('hi')(595,597)
297 | PsiElement(Py:PYXL TAGEND >)('>')(597,598)
298 | PyStringLiteralExpression: (598,612)
299 | PsiElement(Py:PYXL STRING)('\n\n ')(598,612)
300 | Pyxl Tag: null(612,618)
301 | PsiElement(Py:PYXL TAGBEGIN <)('<')(612,613)
302 | PyCallExpression: hi(613,615)
303 | PyClassTagReference: x_hi(613,615)
304 | PsiElement(Py:PYXL TAGNAME)('hi')(613,615)
305 | PsiWhiteSpace(' ')(615,616)
306 | PyxlArgumentList(616,616)
307 |
308 | PsiElement(Py:PYXL TAGENDANDCLOSE />)('/>')(616,618)
309 | PyStringLiteralExpression: (618,627)
310 | PsiElement(Py:PYXL STRING)('\n ')(618,627)
311 | PsiElement(Py:PYXL TAGCLOSE )('')(627,629)
312 | PyCallExpression: hi(629,631)
313 | PyClassTagReference: x_hi(629,631)
314 | PsiElement(Py:PYXL TAGNAME)('hi')(629,631)
315 | PsiElement(Py:PYXL TAGEND >)('>')(631,632)
316 | PsiWhiteSpace('\n ')(632,637)
317 | PsiElement(Py:RPAR)(')')(637,638)
318 | PsiWhiteSpace('\n\n ')(638,644)
319 | PyReturnStatement(644,714)
320 | PsiElement(Py:RETURN_KEYWORD)('return')(644,650)
321 | PsiWhiteSpace(' ')(650,651)
322 | PyParenthesizedExpression(651,714)
323 | PsiElement(Py:LPAR)('(')(651,652)
324 | PsiWhiteSpace(' ')(652,653)
325 | Pyxl Tag: null(653,713)
326 | PsiElement(Py:PYXL TAGBEGIN <)('<')(653,654)
327 | PyCallExpression: select_option(654,667)
328 | PyClassTagReference: x_select_option(654,667)
329 | PsiElement(Py:PYXL TAGNAME)('select_option')(654,667)
330 | PsiWhiteSpace(' ')(667,668)
331 | PyKeywordArgumentImpl: null(668,675)
332 | PyxlAttrName: (668,673)
333 | PsiElement(Py:PYXL ATTRNAME)('value')(668,673)
334 | PsiElement(Py:EQ)('=')(673,674)
335 | PsiElement(Py:PYXL ATTRVALUE)('1')(674,675)
336 | PsiElement(Py:PYXL TAGEND >)('>')(675,676)
337 | PyArgumentList(676,697)
338 | PyStringLiteralExpression: (676,697)
339 | PsiElement(Py:PYXL STRING)('\n One\n ')(676,697)
340 | PsiElement(Py:PYXL TAGCLOSE )('')(697,699)
341 | PyCallExpression: select_option(699,712)
342 | PyClassTagReference: x_select_option(699,712)
343 | PsiElement(Py:PYXL TAGNAME)('select_option')(699,712)
344 | PsiElement(Py:PYXL TAGEND >)('>')(712,713)
345 | PsiElement(Py:RPAR)(')')(713,714)
346 | PsiWhiteSpace('\n\n')(714,716)
347 | PyFunction('goo')(716,978)
348 | PsiElement(Py:DEF_KEYWORD)('def')(716,719)
349 | PsiWhiteSpace(' ')(719,720)
350 | PsiElement(Py:IDENTIFIER)('goo')(720,723)
351 | PyParameterList(723,725)
352 | PsiElement(Py:LPAR)('(')(723,724)
353 | PsiElement(Py:RPAR)(')')(724,725)
354 | PsiElement(Py:COLON)(':')(725,726)
355 | PsiWhiteSpace('\n ')(726,731)
356 | PyStatementList(731,978)
357 | PyAssignmentStatement(731,739)
358 | PyTargetExpression: b(731,732)
359 | PsiElement(Py:IDENTIFIER)('b')(731,732)
360 | PsiWhiteSpace(' ')(732,733)
361 | PsiElement(Py:EQ)('=')(733,734)
362 | PsiWhiteSpace(' ')(734,735)
363 | PyReferenceExpression: True(735,739)
364 | PsiElement(Py:IDENTIFIER)('True')(735,739)
365 | PsiWhiteSpace('\n ')(739,744)
366 | PyReturnStatement(744,978)
367 | PsiElement(Py:RETURN_KEYWORD)('return')(744,750)
368 | PsiWhiteSpace(' ')(750,751)
369 | PyParenthesizedExpression(751,978)
370 | PsiElement(Py:LPAR)('(')(751,752)
371 | Pyxl Tag: null(752,972)
372 | PsiElement(Py:PYXL TAGBEGIN <)('<')(752,753)
373 | PyCallExpression: hi(753,755)
374 | PyClassTagReference: x_hi(753,755)
375 | PsiElement(Py:PYXL TAGNAME)('hi')(753,755)
376 | PsiWhiteSpace('\n ')(755,764)
377 | PyKeywordArgumentImpl: null(764,838)
378 | PyxlAttrName: (764,769)
379 | PsiElement(Py:PYXL ATTRNAME)('class')(764,769)
380 | PsiElement(Py:EQ)('=')(769,770)
381 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(770,771)
382 | PsiElement(Py:PYXL ATTRVALUE)('string over two lines without backslash\n ')(771,819)
383 | PsiElement(Py:PYXL PYTHON EMBED BEGIN {)('{')(819,820)
384 | PyConditionalExpression(820,836)
385 | PyStringLiteralExpression: a(820,823)
386 | PsiElement(Py:SINGLE_QUOTED_STRING)(''a'')(820,823)
387 | PsiWhiteSpace(' ')(823,824)
388 | PsiElement(Py:IF_KEYWORD)('if')(824,826)
389 | PsiWhiteSpace(' ')(826,827)
390 | PyReferenceExpression: b(827,828)
391 | PsiElement(Py:IDENTIFIER)('b')(827,828)
392 | PsiWhiteSpace(' ')(828,829)
393 | PsiElement(Py:ELSE_KEYWORD)('else')(829,833)
394 | PsiWhiteSpace(' ')(833,834)
395 | PyStringLiteralExpression: (834,836)
396 | PsiElement(Py:SINGLE_QUOTED_STRING)('''')(834,836)
397 | PsiElement(Py:PYXL PYTHON EMBED END })('}')(836,837)
398 | PsiElement(Py:PYXL ATTRVALUE END)('"')(837,838)
399 | PsiWhiteSpace('\n ')(838,847)
400 | PyKeywordArgumentImpl: null(847,859)
401 | PyxlAttrName: (847,852)
402 | PsiElement(Py:PYXL ATTRNAME)('prop2')(847,852)
403 | PsiElement(Py:EQ)('=')(852,853)
404 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(853,854)
405 | PsiElement(Py:PYXL ATTRVALUE)('true')(854,858)
406 | PsiElement(Py:PYXL ATTRVALUE END)('"')(858,859)
407 | PsiWhiteSpace(' ')(859,860)
408 | PyKeywordArgumentImpl: null(860,876)
409 | PyxlAttrName: (860,867)
410 | PsiElement(Py:PYXL ATTRNAME)('nohover')(860,867)
411 | PsiElement(Py:EQ)('=')(867,868)
412 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(868,869)
413 | PsiElement(Py:PYXL PYTHON EMBED BEGIN {)('{')(869,870)
414 | PyReferenceExpression: True(870,874)
415 | PsiElement(Py:IDENTIFIER)('True')(870,874)
416 | PsiElement(Py:PYXL PYTHON EMBED END })('}')(874,875)
417 | PsiElement(Py:PYXL ATTRVALUE END)('"')(875,876)
418 | PsiWhiteSpace('\n ')(876,885)
419 | PyKeywordArgumentImpl: null(885,934)
420 | PyxlAttrName: (885,888)
421 | PsiElement(Py:PYXL ATTRNAME)('zoo')(885,888)
422 | PsiElement(Py:EQ)('=')(888,889)
423 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(889,890)
424 | PsiElement(Py:PYXL ATTRVALUE)('string with backslash \\n asdfjkl')(890,933)
425 | PsiElement(Py:PYXL ATTRVALUE END)('"')(933,934)
426 | PsiWhiteSpace('\n ')(934,943)
427 | PsiElement(Py:PYXL TAGEND >)('>')(943,944)
428 | PyArgumentList(944,967)
429 | PyStringLiteralExpression: (944,953)
430 | PsiElement(Py:PYXL STRING)('\n ')(944,953)
431 | PsiElement(Py:PYXL PYTHON EMBED BEGIN {)('{')(953,954)
432 | PyBinaryExpression(954,957)
433 | PyNumericLiteralExpression(954,955)
434 | PsiElement(Py:INTEGER_LITERAL)('5')(954,955)
435 | PsiElement(Py:PLUS)('+')(955,956)
436 | PyNumericLiteralExpression(956,957)
437 | PsiElement(Py:INTEGER_LITERAL)('5')(956,957)
438 | PsiElement(Py:PYXL PYTHON EMBED END })('}')(957,958)
439 | PyStringLiteralExpression: (958,967)
440 | PsiElement(Py:PYXL STRING)('\n ')(958,967)
441 | PsiElement(Py:PYXL TAGCLOSE )('')(967,969)
442 | PyCallExpression: hi(969,971)
443 | PyClassTagReference: x_hi(969,971)
444 | PsiElement(Py:PYXL TAGNAME)('hi')(969,971)
445 | PsiElement(Py:PYXL TAGEND >)('>')(971,972)
446 | PsiWhiteSpace('\n ')(972,977)
447 | PsiElement(Py:RPAR)(')')(977,978)
--------------------------------------------------------------------------------
/testdata/TagNames.py:
--------------------------------------------------------------------------------
1 | # coding: pyxl
2 | regular =
3 | module =
4 |
--------------------------------------------------------------------------------
/testdata/TagNames.txt:
--------------------------------------------------------------------------------
1 | PyFile:TagNames.py(0,56)
2 | PsiComment(Py:END_OF_LINE_COMMENT)('# coding: pyxl')(0,14)
3 | PsiWhiteSpace('\n')(14,15)
4 | PyAssignmentStatement(15,32)
5 | PyTargetExpression: regular(15,22)
6 | PsiElement(Py:IDENTIFIER)('regular')(15,22)
7 | PsiWhiteSpace(' ')(22,23)
8 | PsiElement(Py:EQ)('=')(23,24)
9 | PsiWhiteSpace(' ')(24,25)
10 | Pyxl Tag: null(25,32)
11 | PsiElement(Py:PYXL TAGBEGIN <)('<')(25,26)
12 | PyCallExpression: div(26,29)
13 | PyClassTagReference: x_div(26,29)
14 | PsiElement(Py:PYXL TAGNAME)('div')(26,29)
15 | PsiWhiteSpace(' ')(29,30)
16 | PyxlArgumentList(30,30)
17 |
18 | PsiElement(Py:PYXL TAGENDANDCLOSE />)('/>')(30,32)
19 | PsiWhiteSpace('\n')(32,33)
20 | PyAssignmentStatement(33,56)
21 | PyTargetExpression: module(33,39)
22 | PsiElement(Py:IDENTIFIER)('module')(33,39)
23 | PsiWhiteSpace(' ')(39,40)
24 | PsiElement(Py:EQ)('=')(40,41)
25 | PsiWhiteSpace(' ')(41,42)
26 | Pyxl Tag: null(42,56)
27 | PsiElement(Py:PYXL TAGBEGIN <)('<')(42,43)
28 | PyCallExpression: module.tag(43,53)
29 | PyClassTagReference: x_tag(43,53)
30 | PyReferenceExpression: module(43,49)
31 | PsiElement(Py:PYXL TAGNAME_MODULE)('module')(43,49)
32 | PsiElement(Py:DOT)('.')(49,50)
33 | PsiElement(Py:PYXL TAGNAME)('tag')(50,53)
34 | PsiWhiteSpace(' ')(53,54)
35 | PyxlArgumentList(54,54)
36 |
37 | PsiElement(Py:PYXL TAGENDANDCLOSE />)('/>')(54,56)
--------------------------------------------------------------------------------
/testdata/WithStatements.py:
--------------------------------------------------------------------------------
1 | # coding: pyxl
2 |
3 | with open('file.txt', 'r') as file:
4 | print file.readline()
5 |
6 | with open('foo.txt'), open('bar.txt'):
7 | print "baz"
8 |
--------------------------------------------------------------------------------
/testdata/WithStatements.txt:
--------------------------------------------------------------------------------
1 | PyFile:WithStatements.py(0,133)
2 | PsiComment(Py:END_OF_LINE_COMMENT)('# coding: pyxl')(0,14)
3 | PsiWhiteSpace('\n\n')(14,16)
4 | PyWithStatement(16,77)
5 | PsiElement(Py:WITH_KEYWORD)('with')(16,20)
6 | PsiWhiteSpace(' ')(20,21)
7 | PyWithItem(21,50)
8 | PyCallExpression: open(21,42)
9 | PyReferenceExpression: open(21,25)
10 | PsiElement(Py:IDENTIFIER)('open')(21,25)
11 | PyArgumentList(25,42)
12 | PsiElement(Py:LPAR)('(')(25,26)
13 | PyStringLiteralExpression: file.txt(26,36)
14 | PsiElement(Py:SINGLE_QUOTED_STRING)(''file.txt'')(26,36)
15 | PsiElement(Py:COMMA)(',')(36,37)
16 | PsiWhiteSpace(' ')(37,38)
17 | PyStringLiteralExpression: r(38,41)
18 | PsiElement(Py:SINGLE_QUOTED_STRING)(''r'')(38,41)
19 | PsiElement(Py:RPAR)(')')(41,42)
20 | PsiWhiteSpace(' ')(42,43)
21 | PsiElement(Py:AS_KEYWORD)('as')(43,45)
22 | PsiWhiteSpace(' ')(45,46)
23 | PyTargetExpression: file(46,50)
24 | PsiElement(Py:IDENTIFIER)('file')(46,50)
25 | PsiElement(Py:COLON)(':')(50,51)
26 | PsiWhiteSpace('\n ')(51,56)
27 | PyStatementList(56,77)
28 | PyPrintStatement(56,77)
29 | PsiElement(Py:PRINT_KEYWORD)('print')(56,61)
30 | PsiWhiteSpace(' ')(61,62)
31 | PyCallExpression: file.readline(62,77)
32 | PyReferenceExpression: readline(62,75)
33 | PyReferenceExpression: file(62,66)
34 | PsiElement(Py:IDENTIFIER)('file')(62,66)
35 | PsiElement(Py:DOT)('.')(66,67)
36 | PsiElement(Py:IDENTIFIER)('readline')(67,75)
37 | PyArgumentList(75,77)
38 | PsiElement(Py:LPAR)('(')(75,76)
39 | PsiElement(Py:RPAR)(')')(76,77)
40 | PsiWhiteSpace('\n\n')(77,79)
41 | PyWithStatement(79,133)
42 | PsiElement(Py:WITH_KEYWORD)('with')(79,83)
43 | PsiWhiteSpace(' ')(83,84)
44 | PyWithItem(84,116)
45 | PyTupleExpression(84,116)
46 | PyCallExpression: open(84,99)
47 | PyReferenceExpression: open(84,88)
48 | PsiElement(Py:IDENTIFIER)('open')(84,88)
49 | PyArgumentList(88,99)
50 | PsiElement(Py:LPAR)('(')(88,89)
51 | PyStringLiteralExpression: foo.txt(89,98)
52 | PsiElement(Py:SINGLE_QUOTED_STRING)(''foo.txt'')(89,98)
53 | PsiElement(Py:RPAR)(')')(98,99)
54 | PsiElement(Py:COMMA)(',')(99,100)
55 | PsiWhiteSpace(' ')(100,101)
56 | PyCallExpression: open(101,116)
57 | PyReferenceExpression: open(101,105)
58 | PsiElement(Py:IDENTIFIER)('open')(101,105)
59 | PyArgumentList(105,116)
60 | PsiElement(Py:LPAR)('(')(105,106)
61 | PyStringLiteralExpression: bar.txt(106,115)
62 | PsiElement(Py:SINGLE_QUOTED_STRING)(''bar.txt'')(106,115)
63 | PsiElement(Py:RPAR)(')')(115,116)
64 | PsiElement(Py:COLON)(':')(116,117)
65 | PsiWhiteSpace('\n ')(117,122)
66 | PyStatementList(122,133)
67 | PyPrintStatement(122,133)
68 | PsiElement(Py:PRINT_KEYWORD)('print')(122,127)
69 | PsiWhiteSpace(' ')(127,128)
70 | PyStringLiteralExpression: baz(128,133)
71 | PsiElement(Py:SINGLE_QUOTED_STRING)('"baz"')(128,133)
--------------------------------------------------------------------------------
/testdata/class_self_ref.py:
--------------------------------------------------------------------------------
1 | # coding: pyxl
2 | class x_g():
3 | pass
4 |
5 | class x_cool_class():
6 | foo = True
7 | def renderFooter(self):
8 | if self.foo:
9 | return {self.footer_items}
10 |
--------------------------------------------------------------------------------
/testdata/class_self_ref.txt:
--------------------------------------------------------------------------------
1 | PyFile:class_self_ref.py(0,169)
2 | PsiComment(Py:END_OF_LINE_COMMENT)('# coding: pyxl')(0,14)
3 | PsiWhiteSpace('\n')(14,15)
4 | PyClass: x_g(15,36)
5 | PsiElement(Py:CLASS_KEYWORD)('class')(15,20)
6 | PsiWhiteSpace(' ')(20,21)
7 | PsiElement(Py:IDENTIFIER)('x_g')(21,24)
8 | PyArgumentList(24,26)
9 | PsiElement(Py:LPAR)('(')(24,25)
10 | PsiElement(Py:RPAR)(')')(25,26)
11 | PsiElement(Py:COLON)(':')(26,27)
12 | PsiWhiteSpace('\n ')(27,32)
13 | PyStatementList(32,36)
14 | PyPassStatement(32,36)
15 | PsiElement(Py:PASS_KEYWORD)('pass')(32,36)
16 | PsiWhiteSpace('\n\n')(36,38)
17 | PyClass: x_cool_class(38,169)
18 | PsiElement(Py:CLASS_KEYWORD)('class')(38,43)
19 | PsiWhiteSpace(' ')(43,44)
20 | PsiElement(Py:IDENTIFIER)('x_cool_class')(44,56)
21 | PyArgumentList(56,58)
22 | PsiElement(Py:LPAR)('(')(56,57)
23 | PsiElement(Py:RPAR)(')')(57,58)
24 | PsiElement(Py:COLON)(':')(58,59)
25 | PsiWhiteSpace('\n ')(59,64)
26 | PyStatementList(64,169)
27 | PyAssignmentStatement(64,74)
28 | PyTargetExpression: foo(64,67)
29 | PsiElement(Py:IDENTIFIER)('foo')(64,67)
30 | PsiWhiteSpace(' ')(67,68)
31 | PsiElement(Py:EQ)('=')(68,69)
32 | PsiWhiteSpace(' ')(69,70)
33 | PyReferenceExpression: True(70,74)
34 | PsiElement(Py:IDENTIFIER)('True')(70,74)
35 | PsiWhiteSpace('\n ')(74,79)
36 | PyFunction('renderFooter')(79,169)
37 | PsiElement(Py:DEF_KEYWORD)('def')(79,82)
38 | PsiWhiteSpace(' ')(82,83)
39 | PsiElement(Py:IDENTIFIER)('renderFooter')(83,95)
40 | PyParameterList(95,101)
41 | PsiElement(Py:LPAR)('(')(95,96)
42 | PyNamedParameter('self')(96,100)
43 | PsiElement(Py:IDENTIFIER)('self')(96,100)
44 | PsiElement(Py:RPAR)(')')(100,101)
45 | PsiElement(Py:COLON)(':')(101,102)
46 | PsiWhiteSpace('\n ')(102,111)
47 | PyStatementList(111,169)
48 | PyIfStatement(111,169)
49 | PyIfPartIf(111,169)
50 | PsiElement(Py:IF_KEYWORD)('if')(111,113)
51 | PsiWhiteSpace(' ')(113,114)
52 | PyReferenceExpression: foo(114,122)
53 | PyReferenceExpression: self(114,118)
54 | PsiElement(Py:IDENTIFIER)('self')(114,118)
55 | PsiElement(Py:DOT)('.')(118,119)
56 | PsiElement(Py:IDENTIFIER)('foo')(119,122)
57 | PsiElement(Py:COLON)(':')(122,123)
58 | PsiWhiteSpace('\n ')(123,136)
59 | PyStatementList(136,169)
60 | PyReturnStatement(136,169)
61 | PsiElement(Py:RETURN_KEYWORD)('return')(136,142)
62 | PsiWhiteSpace(' ')(142,143)
63 | Pyxl Tag: null(143,169)
64 | PsiElement(Py:PYXL TAGBEGIN <)('<')(143,144)
65 | PyCallExpression: g(144,145)
66 | PyClassTagReference: x_g(144,145)
67 | PsiElement(Py:PYXL TAGNAME)('g')(144,145)
68 | PsiElement(Py:PYXL TAGEND >)('>')(145,146)
69 | PyArgumentList(146,165)
70 | PsiElement(Py:PYXL PYTHON EMBED BEGIN {)('{')(146,147)
71 | PyReferenceExpression: footer_items(147,164)
72 | PyReferenceExpression: self(147,151)
73 | PsiElement(Py:IDENTIFIER)('self')(147,151)
74 | PsiElement(Py:DOT)('.')(151,152)
75 | PsiElement(Py:IDENTIFIER)('footer_items')(152,164)
76 | PsiElement(Py:PYXL PYTHON EMBED END })('}')(164,165)
77 | PsiElement(Py:PYXL TAGCLOSE )('')(165,167)
78 | PyCallExpression: g(167,168)
79 | PyClassTagReference: x_g(167,168)
80 | PsiElement(Py:PYXL TAGNAME)('g')(167,168)
81 | PsiElement(Py:PYXL TAGEND >)('>')(168,169)
--------------------------------------------------------------------------------
/testdata/nestedembed.py:
--------------------------------------------------------------------------------
1 | # coding: pyxl
2 |
3 | class x_a():
4 | pass
5 |
6 | class x_b():
7 | pass
8 |
9 | def foo():
10 | zoo = {'choice1':'value1', }
11 | return (
12 |
13 | {[
14 | {v}
15 | for k, v in zoo.items()
16 | ]}
17 |
18 | )
--------------------------------------------------------------------------------
/testdata/nestedembed.txt:
--------------------------------------------------------------------------------
1 | PyFile:nestedembed.py(0,274)
2 | PsiComment(Py:END_OF_LINE_COMMENT)('# coding: pyxl')(0,14)
3 | PsiWhiteSpace('\n\n')(14,16)
4 | PyClass: x_a(16,37)
5 | PsiElement(Py:CLASS_KEYWORD)('class')(16,21)
6 | PsiWhiteSpace(' ')(21,22)
7 | PsiElement(Py:IDENTIFIER)('x_a')(22,25)
8 | PyArgumentList(25,27)
9 | PsiElement(Py:LPAR)('(')(25,26)
10 | PsiElement(Py:RPAR)(')')(26,27)
11 | PsiElement(Py:COLON)(':')(27,28)
12 | PsiWhiteSpace('\n ')(28,33)
13 | PyStatementList(33,37)
14 | PyPassStatement(33,37)
15 | PsiElement(Py:PASS_KEYWORD)('pass')(33,37)
16 | PsiWhiteSpace('\n\n')(37,39)
17 | PyClass: x_b(39,60)
18 | PsiElement(Py:CLASS_KEYWORD)('class')(39,44)
19 | PsiWhiteSpace(' ')(44,45)
20 | PsiElement(Py:IDENTIFIER)('x_b')(45,48)
21 | PyArgumentList(48,50)
22 | PsiElement(Py:LPAR)('(')(48,49)
23 | PsiElement(Py:RPAR)(')')(49,50)
24 | PsiElement(Py:COLON)(':')(50,51)
25 | PsiWhiteSpace('\n ')(51,56)
26 | PyStatementList(56,60)
27 | PyPassStatement(56,60)
28 | PsiElement(Py:PASS_KEYWORD)('pass')(56,60)
29 | PsiWhiteSpace('\n\n')(60,62)
30 | PyFunction('foo')(62,274)
31 | PsiElement(Py:DEF_KEYWORD)('def')(62,65)
32 | PsiWhiteSpace(' ')(65,66)
33 | PsiElement(Py:IDENTIFIER)('foo')(66,69)
34 | PyParameterList(69,71)
35 | PsiElement(Py:LPAR)('(')(69,70)
36 | PsiElement(Py:RPAR)(')')(70,71)
37 | PsiElement(Py:COLON)(':')(71,72)
38 | PsiWhiteSpace('\n ')(72,77)
39 | PyStatementList(77,274)
40 | PyAssignmentStatement(77,105)
41 | PyTargetExpression: zoo(77,80)
42 | PsiElement(Py:IDENTIFIER)('zoo')(77,80)
43 | PsiWhiteSpace(' ')(80,81)
44 | PsiElement(Py:EQ)('=')(81,82)
45 | PsiWhiteSpace(' ')(82,83)
46 | PyDictLiteralExpression(83,105)
47 | PsiElement(Py:LBRACE)('{')(83,84)
48 | PyKeyValueExpression(84,102)
49 | PyStringLiteralExpression: choice1(84,93)
50 | PsiElement(Py:SINGLE_QUOTED_STRING)(''choice1'')(84,93)
51 | PsiElement(Py:COLON)(':')(93,94)
52 | PyStringLiteralExpression: value1(94,102)
53 | PsiElement(Py:SINGLE_QUOTED_STRING)(''value1'')(94,102)
54 | PsiElement(Py:COMMA)(',')(102,103)
55 | PsiWhiteSpace(' ')(103,104)
56 | PsiElement(Py:RBRACE)('}')(104,105)
57 | PsiWhiteSpace('\n ')(105,110)
58 | PyReturnStatement(110,274)
59 | PsiElement(Py:RETURN_KEYWORD)('return')(110,116)
60 | PsiWhiteSpace(' ')(116,117)
61 | PyParenthesizedExpression(117,274)
62 | PsiElement(Py:LPAR)('(')(117,118)
63 | PsiWhiteSpace('\n ')(118,127)
64 | Pyxl Tag: null(127,268)
65 | PsiElement(Py:PYXL TAGBEGIN <)('<')(127,128)
66 | PyCallExpression: a(128,129)
67 | PyClassTagReference: x_a(128,129)
68 | PsiElement(Py:PYXL TAGNAME)('a')(128,129)
69 | PsiWhiteSpace(' ')(129,130)
70 | PyKeywordArgumentImpl: null(130,141)
71 | PyxlAttrName: (130,134)
72 | PsiElement(Py:PYXL ATTRNAME)('name')(130,134)
73 | PsiElement(Py:EQ)('=')(134,135)
74 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(135,136)
75 | PsiElement(Py:PYXL ATTRVALUE)('abcd')(136,140)
76 | PsiElement(Py:PYXL ATTRVALUE END)('"')(140,141)
77 | PsiElement(Py:PYXL TAGEND >)('>')(141,142)
78 | PyArgumentList(142,264)
79 | PyStringLiteralExpression: (142,155)
80 | PsiElement(Py:PYXL STRING)('\n ')(142,155)
81 | PsiElement(Py:PYXL PYTHON EMBED BEGIN {)('{')(155,156)
82 | PyListCompExpression(156,254)
83 | PsiElement(Py:LBRACKET)('[')(156,157)
84 | PsiWhiteSpace('\n ')(157,174)
85 | Pyxl Tag: null(174,196)
86 | PsiElement(Py:PYXL TAGBEGIN <)('<')(174,175)
87 | PyCallExpression: b(175,176)
88 | PyClassTagReference: x_b(175,176)
89 | PsiElement(Py:PYXL TAGNAME)('b')(175,176)
90 | PsiWhiteSpace(' ')(176,177)
91 | PyKeywordArgumentImpl: null(177,188)
92 | PyxlAttrName: (177,182)
93 | PsiElement(Py:PYXL ATTRNAME)('value')(177,182)
94 | PsiElement(Py:EQ)('=')(182,183)
95 | PsiElement(Py:PYXL ATTRVALUE BEGIN)('"')(183,184)
96 | PsiElement(Py:PYXL PYTHON EMBED BEGIN {)('{')(184,185)
97 | PyReferenceExpression: k(185,186)
98 | PsiElement(Py:IDENTIFIER)('k')(185,186)
99 | PsiElement(Py:PYXL PYTHON EMBED END })('}')(186,187)
100 | PsiElement(Py:PYXL ATTRVALUE END)('"')(187,188)
101 | PsiElement(Py:PYXL TAGEND >)('>')(188,189)
102 | PyArgumentList(189,192)
103 | PsiElement(Py:PYXL PYTHON EMBED BEGIN {)('{')(189,190)
104 | PyReferenceExpression: v(190,191)
105 | PsiElement(Py:IDENTIFIER)('v')(190,191)
106 | PsiElement(Py:PYXL PYTHON EMBED END })('}')(191,192)
107 | PsiElement(Py:PYXL TAGCLOSE )('')(192,194)
108 | PyCallExpression: b(194,195)
109 | PyClassTagReference: x_b(194,195)
110 | PsiElement(Py:PYXL TAGNAME)('b')(194,195)
111 | PsiElement(Py:PYXL TAGEND >)('>')(195,196)
112 | PsiWhiteSpace('\n ')(196,217)
113 | PsiElement(Py:FOR_KEYWORD)('for')(217,220)
114 | PsiWhiteSpace(' ')(220,221)
115 | PyTupleExpression(221,225)
116 | PyTargetExpression: k(221,222)
117 | PsiElement(Py:IDENTIFIER)('k')(221,222)
118 | PsiElement(Py:COMMA)(',')(222,223)
119 | PsiWhiteSpace(' ')(223,224)
120 | PyTargetExpression: v(224,225)
121 | PsiElement(Py:IDENTIFIER)('v')(224,225)
122 | PsiWhiteSpace(' ')(225,226)
123 | PsiElement(Py:IN_KEYWORD)('in')(226,228)
124 | PsiWhiteSpace(' ')(228,229)
125 | PyCallExpression: zoo.items(229,240)
126 | PyReferenceExpression: items(229,238)
127 | PyReferenceExpression: zoo(229,232)
128 | PsiElement(Py:IDENTIFIER)('zoo')(229,232)
129 | PsiElement(Py:DOT)('.')(232,233)
130 | PsiElement(Py:IDENTIFIER)('items')(233,238)
131 | PyArgumentList(238,240)
132 | PsiElement(Py:LPAR)('(')(238,239)
133 | PsiElement(Py:RPAR)(')')(239,240)
134 | PsiWhiteSpace('\n ')(240,253)
135 | PsiElement(Py:RBRACKET)(']')(253,254)
136 | PsiElement(Py:PYXL PYTHON EMBED END })('}')(254,255)
137 | PyStringLiteralExpression: (255,264)
138 | PsiElement(Py:PYXL STRING)('\n ')(255,264)
139 | PsiElement(Py:PYXL TAGCLOSE )('')(264,266)
140 | PyCallExpression: a(266,267)
141 | PyClassTagReference: x_a(266,267)
142 | PsiElement(Py:PYXL TAGNAME)('a')(266,267)
143 | PsiElement(Py:PYXL TAGEND >)('>')(267,268)
144 | PsiWhiteSpace('\n ')(268,273)
145 | PsiElement(Py:RPAR)(')')(273,274)
--------------------------------------------------------------------------------
/tests/PyxlParsingTest.java:
--------------------------------------------------------------------------------
1 |
2 | import com.christofferklang.pyxl.parsing.PyxlParserDefinition;
3 | import com.intellij.testFramework.ParsingTestCase;
4 | import com.jetbrains.python.PythonDialectsTokenSetContributor;
5 | import com.jetbrains.python.PythonTokenSetContributor;
6 |
7 | public class PyxlParsingTest extends ParsingTestCase {
8 | /**
9 | * These tests work like this:
10 | * - They look at the name of the method, without the test-part to
11 | * figure out which files to use under testdata.
12 | *
13 | * I.e. adding a method called testMyTest() { doTest(true); } here
14 | * will run the file "testdata/MyTest.py" through the parser and expect the output
15 | * from that run to match the PSI tree decribed in "testdata/MyTest.txt".
16 | *
17 | * If the text file is not there on the first run of the test, one will be created
18 | * with the _current_ parser definition.
19 | */
20 |
21 | public void testParsingTestData() {
22 | doTest(true);
23 | }
24 |
25 | public void testnestedembed() {
26 | doTest(true);
27 | }
28 |
29 | public void testclass_self_ref() {
30 | doTest(true);
31 | }
32 |
33 | public void testWithStatements() {
34 | doTest(true);
35 | }
36 |
37 | public void testComments() {
38 | doTest(true);
39 | }
40 |
41 | public void testTagNames() {
42 | doTest(true);
43 | }
44 |
45 | public void testAttributes() {
46 | doTest(true);
47 | }
48 |
49 | public PyxlParsingTest() {
50 | super("", "py", new PyxlParserDefinition());
51 | }
52 |
53 | @Override
54 | protected void setUp() throws Exception {
55 | super.setUp();
56 | // Following lines learned from python plugin's test case - PythonParsingTest.java
57 | registerExtensionPoint(PythonDialectsTokenSetContributor.EP_NAME, PythonDialectsTokenSetContributor.class);
58 | registerExtension(PythonDialectsTokenSetContributor.EP_NAME, new PythonTokenSetContributor());
59 |
60 | }
61 | @Override
62 | protected String getTestDataPath() {
63 | return "../pycharm-pyxl/testdata/";
64 | }
65 |
66 | @Override
67 | protected boolean skipSpaces() {
68 | return false;
69 | }
70 |
71 | @Override
72 | protected boolean includeRanges() {
73 | return true;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------