2 * Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved.
3 * Use is subject to license terms.
5 package com
.intellij
.psi
.templateLanguages
;
7 import com
.intellij
.lang
.ASTNode
;
8 import com
.intellij
.lang
.Language
;
9 import com
.intellij
.lang
.LanguageParserDefinitions
;
10 import com
.intellij
.lang
.LanguageExtension
;
11 import com
.intellij
.lexer
.Lexer
;
12 import com
.intellij
.lexer
.MergingLexerAdapter
;
13 import com
.intellij
.openapi
.fileTypes
.LanguageFileType
;
14 import com
.intellij
.openapi
.diagnostic
.Logger
;
15 import com
.intellij
.psi
.FileViewProvider
;
16 import com
.intellij
.psi
.PsiFile
;
17 import com
.intellij
.psi
.PsiManager
;
18 import com
.intellij
.psi
.SingleRootFileViewProvider
;
19 import com
.intellij
.psi
.impl
.source
.DummyHolder
;
20 import com
.intellij
.psi
.impl
.source
.PsiFileImpl
;
21 import com
.intellij
.psi
.impl
.source
.parsing
.ChameleonTransforming
;
22 import com
.intellij
.psi
.impl
.source
.tree
.*;
23 import com
.intellij
.psi
.tree
.IElementType
;
24 import com
.intellij
.psi
.tree
.IFileElementType
;
25 import com
.intellij
.psi
.tree
.TokenSet
;
26 import com
.intellij
.testFramework
.LightVirtualFile
;
27 import com
.intellij
.util
.CharTable
;
28 import com
.intellij
.util
.LocalTimeCounter
;
29 import org
.jetbrains
.annotations
.NonNls
;
30 import org
.jetbrains
.annotations
.NotNull
;
31 import org
.jetbrains
.annotations
.Nullable
;
38 public class TemplateDataElementType
extends IFileElementType
implements ITemplateDataElementType
{
39 private static final Logger LOG
= Logger
.getInstance("#com.intellij.psi.templateLanguages.TemplateDataElementType");
41 private final static LanguageExtension
<TreePatcher
> TREE_PATCHER
= new LanguageExtension
<TreePatcher
>("com.intellij.lang.treePatcher", new SimpleTreePatcher());
43 private final IElementType myTemplateElementType
;
44 private final IElementType myOuterElementType
;
46 public TemplateDataElementType(@NonNls String debugName
, Language language
, IElementType templateElementType
, IElementType outerElementType
) {
47 super(debugName
, language
);
48 myTemplateElementType
= templateElementType
;
49 myOuterElementType
= outerElementType
;
52 protected Lexer
createBaseLexer(TemplateLanguageFileViewProvider viewProvider
) {
53 return LanguageParserDefinitions
.INSTANCE
.forLanguage(viewProvider
.getBaseLanguage()).createLexer(viewProvider
.getManager().getProject());
56 protected LanguageFileType
createTemplateFakeFileType(final Language language
) {
57 return new TemplateFileType(language
);
60 public ASTNode
parseContents(ASTNode chameleon
) {
61 final CharTable table
= SharedImplUtil
.findCharTableByTree(chameleon
);
62 final FileElement treeElement
= new DummyHolder(((TreeElement
)chameleon
).getManager(), null, table
).getTreeElement();
63 final PsiFile file
= (PsiFile
)TreeUtil
.getFileElement((TreeElement
)chameleon
).getPsi();
64 PsiFile originalFile
= file
.getOriginalFile();
65 if (originalFile
== null) originalFile
= file
;
67 final TemplateLanguageFileViewProvider viewProvider
= (TemplateLanguageFileViewProvider
)originalFile
.getViewProvider();
69 final Language language
= viewProvider
.getTemplateDataLanguage();
70 final CharSequence chars
= ((LeafElement
)chameleon
).getInternedText();
72 final Lexer baseLexer
= createBaseLexer(viewProvider
);
73 final CharSequence templateText
= createTemplateText(chars
, baseLexer
);
74 final PsiFile templateFile
= createFromText(language
, templateText
, file
.getManager());
76 final TreeElement parsed
= ((PsiFileImpl
)templateFile
).calcTreeElement();
77 ChameleonTransforming
.transformChildren(parsed
, false);
79 Lexer langLexer
= LanguageParserDefinitions
.INSTANCE
.forLanguage(language
).createLexer(file
.getProject());
80 final Lexer lexer
= new MergingLexerAdapter(
81 new TemplateBlackAndWhiteLexer(createBaseLexer(viewProvider
), langLexer
, myTemplateElementType
, myOuterElementType
),
82 TokenSet
.create(myTemplateElementType
, myOuterElementType
));
83 lexer
.start(chars
, 0, chars
.length(),0);
84 insertOuters(parsed
, lexer
, table
);
87 final TreeElement element
= parsed
.getFirstChildNode();
88 if (element
!= null) {
89 TreeUtil
.addChildren(treeElement
, element
);
93 treeElement
.clearCaches();
94 treeElement
.subtreeChanged();
95 return treeElement
.getFirstChildNode();
98 private CharSequence
createTemplateText(CharSequence buf
, Lexer lexer
) {
99 StringBuilder result
= new StringBuilder(buf
.length());
100 lexer
.start(buf
, 0, buf
.length(), 0);
102 while (lexer
.getTokenType() != null) {
103 if (lexer
.getTokenType() == myTemplateElementType
) {
104 result
.append(buf
, lexer
.getTokenStart(), lexer
.getTokenEnd());
112 private void insertOuters(TreeElement root
, Lexer lexer
, final CharTable table
) {
113 TreePatcher patcher
= TREE_PATCHER
.forLanguage(root
.getPsi().getLanguage());
116 LeafElement leaf
= TreeUtil
.findFirstLeaf(root
);
117 while (lexer
.getTokenType() != null) {
118 IElementType tt
= lexer
.getTokenType();
119 if (tt
!= myTemplateElementType
) {
120 while (leaf
!= null && treeOffset
< lexer
.getTokenStart()) {
121 treeOffset
+= leaf
.getTextLength();
122 if (treeOffset
> lexer
.getTokenStart()) {
123 if (leaf
instanceof ChameleonElement
) {
124 final ASTNode transformed
= ChameleonTransforming
.transform(leaf
);
125 treeOffset
-= leaf
.getTextLength();
126 leaf
= TreeUtil
.findFirstLeaf(transformed
);
129 leaf
= patcher
.split(leaf
, leaf
.getTextLength() - (treeOffset
- lexer
.getTokenStart()), table
);
130 treeOffset
= lexer
.getTokenStart();
132 leaf
= (LeafElement
)TreeUtil
.nextLeaf(leaf
);
135 if (leaf
== null) break;
137 final OuterLanguageElementImpl newLeaf
= createOuterLanguageElement(lexer
, table
, myOuterElementType
);
138 patcher
.insert(leaf
.getTreeParent(), leaf
, newLeaf
);
139 leaf
.getTreeParent().subtreeChanged();
145 if (lexer
.getTokenType() != null) {
146 assert lexer
.getTokenType() != myTemplateElementType
;
147 final OuterLanguageElementImpl newLeaf
= createOuterLanguageElement(lexer
, table
, myOuterElementType
);
148 TreeUtil
.addChildren((CompositeElement
)root
, newLeaf
);
149 ((CompositeElement
)root
).subtreeChanged();
153 protected OuterLanguageElementImpl
createOuterLanguageElement(final Lexer lexer
, final CharTable table
,
154 final IElementType outerElementType
) {
155 final CharSequence buffer
= lexer
.getBufferSequence();
156 final int tokenStart
= lexer
.getTokenStart();
157 if (tokenStart
< 0 || tokenStart
> buffer
.length()) {
158 LOG
.assertTrue(false, "Invalid start: " + tokenStart
+ "; " + lexer
);
160 final int tokenEnd
= lexer
.getTokenEnd();
161 if (tokenEnd
< 0 || tokenEnd
> buffer
.length()) {
162 LOG
.assertTrue(false, "Invalid end: " + tokenEnd
+ "; " + lexer
);
165 return new OuterLanguageElementImpl(outerElementType
, buffer
, tokenStart
, tokenEnd
, table
);
168 private PsiFile
createFromText(final Language language
, CharSequence text
, PsiManager manager
) {
170 final LightVirtualFile virtualFile
= new LightVirtualFile("foo", createTemplateFakeFileType(language
), text
, LocalTimeCounter
.currentTime());
172 FileViewProvider viewProvider
= new SingleRootFileViewProvider(manager
, virtualFile
, false) {
174 public Language
getBaseLanguage() {
179 return viewProvider
.getPsi(language
);
182 protected static class TemplateFileType
extends LanguageFileType
{
183 private final Language myLanguage
;
185 public TemplateFileType(final Language language
) {
187 myLanguage
= language
;
191 public String
getDefaultExtension() {
197 public String
getDescription() {
198 return "fake for language" + myLanguage
.getID();
202 public Icon
getIcon() {
208 public String
getName() {
209 return myLanguage
.getID();