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
.LanguageExtension
;
10 import com
.intellij
.lang
.LanguageParserDefinitions
;
11 import com
.intellij
.lexer
.Lexer
;
12 import com
.intellij
.lexer
.MergingLexerAdapter
;
13 import com
.intellij
.openapi
.diagnostic
.Logger
;
14 import com
.intellij
.openapi
.fileTypes
.LanguageFileType
;
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();
66 final TemplateLanguageFileViewProvider viewProvider
= (TemplateLanguageFileViewProvider
)originalFile
.getViewProvider();
68 final Language language
= viewProvider
.getTemplateDataLanguage();
69 final CharSequence chars
= ((LeafElement
)chameleon
).getInternedText();
71 final Lexer baseLexer
= createBaseLexer(viewProvider
);
72 final CharSequence templateText
= createTemplateText(chars
, baseLexer
);
73 final PsiFile templateFile
= createFromText(language
, templateText
, file
.getManager());
75 final TreeElement parsed
= ((PsiFileImpl
)templateFile
).calcTreeElement();
76 ChameleonTransforming
.transformChildren(parsed
, false);
78 Lexer langLexer
= LanguageParserDefinitions
.INSTANCE
.forLanguage(language
).createLexer(file
.getProject());
79 final Lexer lexer
= new MergingLexerAdapter(
80 new TemplateBlackAndWhiteLexer(createBaseLexer(viewProvider
), langLexer
, myTemplateElementType
, myOuterElementType
),
81 TokenSet
.create(myTemplateElementType
, myOuterElementType
));
82 lexer
.start(chars
, 0, chars
.length(),0);
83 insertOuters(parsed
, lexer
, table
);
86 final TreeElement element
= parsed
.getFirstChildNode();
87 if (element
!= null) {
88 treeElement
.rawAddChildren(element
);
92 treeElement
.clearCaches();
93 treeElement
.subtreeChanged();
94 return treeElement
.getFirstChildNode();
97 private CharSequence
createTemplateText(CharSequence buf
, Lexer lexer
) {
98 StringBuilder result
= new StringBuilder(buf
.length());
99 lexer
.start(buf
, 0, buf
.length(), 0);
101 while (lexer
.getTokenType() != null) {
102 if (lexer
.getTokenType() == myTemplateElementType
) {
103 result
.append(buf
, lexer
.getTokenStart(), lexer
.getTokenEnd());
111 private void insertOuters(TreeElement root
, Lexer lexer
, final CharTable table
) {
112 TreePatcher patcher
= TREE_PATCHER
.forLanguage(root
.getPsi().getLanguage());
115 LeafElement leaf
= TreeUtil
.findFirstLeaf(root
);
116 while (lexer
.getTokenType() != null) {
117 IElementType tt
= lexer
.getTokenType();
118 if (tt
!= myTemplateElementType
) {
119 while (leaf
!= null && treeOffset
< lexer
.getTokenStart()) {
120 treeOffset
+= leaf
.getTextLength();
121 if (treeOffset
> lexer
.getTokenStart()) {
122 if (leaf
instanceof ChameleonElement
) {
123 final ASTNode transformed
= ChameleonTransforming
.transform(leaf
);
124 treeOffset
-= leaf
.getTextLength();
125 leaf
= TreeUtil
.findFirstLeaf(transformed
);
128 leaf
= patcher
.split(leaf
, leaf
.getTextLength() - (treeOffset
- lexer
.getTokenStart()), table
);
129 treeOffset
= lexer
.getTokenStart();
131 leaf
= (LeafElement
)TreeUtil
.nextLeaf(leaf
);
134 if (leaf
== null) break;
136 final OuterLanguageElementImpl newLeaf
= createOuterLanguageElement(lexer
, table
, myOuterElementType
);
137 patcher
.insert(leaf
.getTreeParent(), leaf
, newLeaf
);
138 leaf
.getTreeParent().subtreeChanged();
144 if (lexer
.getTokenType() != null) {
145 assert lexer
.getTokenType() != myTemplateElementType
;
146 final OuterLanguageElementImpl newLeaf
= createOuterLanguageElement(lexer
, table
, myOuterElementType
);
147 ((CompositeElement
)root
).rawAddChildren(newLeaf
);
148 ((CompositeElement
)root
).subtreeChanged();
152 protected OuterLanguageElementImpl
createOuterLanguageElement(final Lexer lexer
, final CharTable table
,
153 final IElementType outerElementType
) {
154 final CharSequence buffer
= lexer
.getBufferSequence();
155 final int tokenStart
= lexer
.getTokenStart();
156 if (tokenStart
< 0 || tokenStart
> buffer
.length()) {
157 LOG
.assertTrue(false, "Invalid start: " + tokenStart
+ "; " + lexer
);
159 final int tokenEnd
= lexer
.getTokenEnd();
160 if (tokenEnd
< 0 || tokenEnd
> buffer
.length()) {
161 LOG
.assertTrue(false, "Invalid end: " + tokenEnd
+ "; " + lexer
);
164 return new OuterLanguageElementImpl(outerElementType
, table
.intern(buffer
, tokenStart
, tokenEnd
));
167 private PsiFile
createFromText(final Language language
, CharSequence text
, PsiManager manager
) {
169 final LightVirtualFile virtualFile
= new LightVirtualFile("foo", createTemplateFakeFileType(language
), text
, LocalTimeCounter
.currentTime());
171 FileViewProvider viewProvider
= new SingleRootFileViewProvider(manager
, virtualFile
, false) {
173 public Language
getBaseLanguage() {
178 return viewProvider
.getPsi(language
);
181 protected static class TemplateFileType
extends LanguageFileType
{
182 private final Language myLanguage
;
184 public TemplateFileType(final Language language
) {
186 myLanguage
= language
;
190 public String
getDefaultExtension() {
196 public String
getDescription() {
197 return "fake for language" + myLanguage
.getID();
201 public Icon
getIcon() {
207 public String
getName() {
208 return myLanguage
.getID();