diff --git a/ExtendedApi/src/com/intellij/testFramework/EditorActionTestCase.java b/ExtendedApi/src/com/intellij/testFramework/EditorActionTestCase.java index f110f49ff34e..8b6d6d0e8846 100644 --- a/ExtendedApi/src/com/intellij/testFramework/EditorActionTestCase.java +++ b/ExtendedApi/src/com/intellij/testFramework/EditorActionTestCase.java @@ -4,6 +4,8 @@ import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.psi.PsiDocumentManager; import java.io.IOException; @@ -40,6 +42,12 @@ public abstract class EditorActionTestCase extends LightCodeInsightTestCase { protected void doTextTest(String fileName, String textBefore, String textAfter, boolean ignoreTrailingSpaces) throws IOException { configureFromFileText(fileName, textBefore); invokeAction(); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); + } + }); + assertEquals("Reparse error!", myEditor.getDocument().getText(), myFile.getText()); checkResultByText(null, textAfter, ignoreTrailingSpaces); } diff --git a/openapi/src/com/intellij/lang/Commenter.java b/openapi/src/com/intellij/lang/Commenter.java new file mode 100644 index 000000000000..515f5dd6eb4a --- /dev/null +++ b/openapi/src/com/intellij/lang/Commenter.java @@ -0,0 +1,16 @@ +package com.intellij.lang; + +import com.intellij.psi.tree.IElementType; + +/** + * @author max + */ +public interface Commenter { + IElementType getLineCommentToken(); + String getLineCommentPrefix(); + boolean isLineCommentPrefixOnZeroColumn(); + + IElementType getBlockCommentToken(); + String getBlockCommentPrefix(); + String getBlockCommentSuffix(); +} diff --git a/openapi/src/com/intellij/lang/Language.java b/openapi/src/com/intellij/lang/Language.java index fd4c9f273fd9..bdc891540cfa 100644 --- a/openapi/src/com/intellij/lang/Language.java +++ b/openapi/src/com/intellij/lang/Language.java @@ -70,6 +70,10 @@ public abstract class Language { return null; } + public Commenter getCommenter() { + return null; + } + public Annotator getAnnotator() { return null; } diff --git a/openapi/src/com/intellij/psi/JavaElementVisitor.java b/openapi/src/com/intellij/psi/JavaElementVisitor.java index 57b23097663c..3ee71e8bd674 100644 --- a/openapi/src/com/intellij/psi/JavaElementVisitor.java +++ b/openapi/src/com/intellij/psi/JavaElementVisitor.java @@ -67,7 +67,7 @@ public abstract class JavaElementVisitor { } public void visitComment(PsiComment comment) { - visitJavaToken(comment); + visitElement(comment); } public void visitConditionalExpression(PsiConditionalExpression expression) { diff --git a/openapi/src/com/intellij/psi/PsiComment.java b/openapi/src/com/intellij/psi/PsiComment.java index c01e81a99b3b..b89b03d3cc4d 100644 --- a/openapi/src/com/intellij/psi/PsiComment.java +++ b/openapi/src/com/intellij/psi/PsiComment.java @@ -4,6 +4,9 @@ */ package com.intellij.psi; +import com.intellij.psi.tree.IElementType; -public interface PsiComment extends PsiJavaToken { + +public interface PsiComment extends PsiElement { + IElementType getTokenType(); } diff --git a/openapi/src/com/intellij/psi/xml/XmlComment.java b/openapi/src/com/intellij/psi/xml/XmlComment.java index c47c4caf7a20..1f2cf61bac88 100644 --- a/openapi/src/com/intellij/psi/xml/XmlComment.java +++ b/openapi/src/com/intellij/psi/xml/XmlComment.java @@ -4,8 +4,10 @@ */ package com.intellij.psi.xml; +import com.intellij.psi.PsiComment; + /** * @author Mike */ -public interface XmlComment extends XmlElement { +public interface XmlComment extends XmlElement, PsiComment { } diff --git a/source/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java b/source/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java index 6cd7392d5539..3482fc58892e 100644 --- a/source/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java +++ b/source/com/intellij/codeInsight/generation/CommentByBlockCommentHandler.java @@ -3,277 +3,36 @@ package com.intellij.codeInsight.generation; import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.featureStatistics.FeatureUsageTracker; +import com.intellij.ide.highlighter.custom.CustomFileTypeLexer; +import com.intellij.ide.highlighter.custom.impl.CustomFileType; +import com.intellij.lang.ASTNode; +import com.intellij.lang.Commenter; +import com.intellij.lang.Language; +import com.intellij.lexer.Lexer; import com.intellij.openapi.editor.*; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.StdFileTypes; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.psi.*; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.CustomHighlighterTokenType; +import com.intellij.psi.PsiComment; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.codeStyle.Indent; -import com.intellij.psi.jsp.JspFile; -import com.intellij.psi.jsp.JspToken; -import com.intellij.psi.jsp.JspTokenType; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.psi.xml.XmlComment; -import com.intellij.psi.xml.XmlFile; -import com.intellij.psi.xml.XmlTag; -import com.intellij.psi.xml.XmlToken; import com.intellij.util.text.CharArrayUtil; -import java.util.HashMap; -import java.util.Map; - -public class CommentByBlockCommentHandler implements CodeInsightActionHandler, BlockCommenter.BlockCommenterContext { +public class CommentByBlockCommentHandler implements CodeInsightActionHandler { private Project myProject; private Editor myEditor; private PsiFile myFile; private Document myDocument; - private static final Map customCommenters = new HashMap(4); - - public static final void registerCommenter(FileType fileType, BlockCommenter blockCommenter) { - customCommenters.put(fileType, blockCommenter); - } - - public static BlockCommenter getCustomCommenter(FileType fileType) { - return customCommenters.get(fileType); - } - - public static BlockCommenter getCommenter(PsiFile file) { - BlockCommenter blockCommenter = customCommenters.get(file.getFileType()); - if (blockCommenter!=null) return blockCommenter; - - if (isJavaFile(file)) { - return new JavaBlockCommenter(); - } else if (file instanceof XmlFile) { - return new XmlBlockCommenter(); - } else if (file instanceof JspFile) { - return new JspBlockCommenter(); - } - - return null; - } - - static { - registerCommenter(StdFileTypes.JAVA,new JavaBlockCommenter()); - registerCommenter(StdFileTypes.JSP,new JspBlockCommenter()); - registerCommenter(StdFileTypes.XML,new XmlBlockCommenter()); - - HtmlBlockCommenter blockCommenter = new HtmlBlockCommenter(); - registerCommenter(StdFileTypes.HTML,blockCommenter); - registerCommenter(StdFileTypes.XHTML,blockCommenter); - registerCommenter(StdFileTypes.JSPX,new JspxBlockCommenter()); - } - - static class JavaBlockCommenter implements BlockCommenter { - public void commentRange(int start, int end, BlockCommenterContext context) { - context.commentRange(start,end,"/*","*/"); - } - - public void uncommentRange(PsiElement element, int commentStart, BlockCommenterContext context) { - if (element instanceof PsiComment && element.getText().startsWith("/*")) { - context.uncommentRange(element.getTextRange(),"/*", "*/"); - } else { - context.insertEmptyComment(commentStart, "/*", "*/"); - } - } - - public Object clone() { - try { - return super.clone(); - } - catch (CloneNotSupportedException e) { - return null; - } - } - } - - static class XmlBlockCommenter implements BlockCommenter { - public void commentRange(int start, int end, BlockCommenterContext context) { - context.commentRange(start, end, ""); - } - - public void uncommentRange(PsiElement element, int commentStart, BlockCommenterContext context) { - if (element.getParent() instanceof XmlComment) { - context.uncommentRange(element.getParent().getTextRange(), ""); - } else { - context.insertEmptyComment(commentStart, ""); - } - } - - public Object clone() { - try { - return super.clone(); - } - catch (CloneNotSupportedException e) { - return null; - } - } - } - - public static class HtmlBlockCommenter implements BlockCommenter { - private static BlockCommenter ourStyleCommenter; - private boolean myInitialized; - private BlockCommenter myCommenterToUse; - private static BlockCommenter ourScriptCommenter; - - private void initialize(PsiElement element, int start, int end, BlockCommenterContext context) { - if (!myInitialized) { - myInitialized = true; - - if (element==null) { - element = PsiUtil.getElementInclusiveRange( - context.getFile(), - new TextRange(start,end) - ); - } - - if (element!=null) { - element = PsiTreeUtil.getParentOfType(element, XmlTag.class,false); - - if (element instanceof XmlTag) { - final String name = ((XmlTag)element).getName(); - - if (name.equalsIgnoreCase("style") && - ourStyleCommenter!=null - ) { - myCommenterToUse = (BlockCommenter)ourStyleCommenter.clone(); - } else if (name.equalsIgnoreCase("script") && ourScriptCommenter!=null) { - myCommenterToUse = (BlockCommenter)ourScriptCommenter.clone(); - } - } - } - - if (myCommenterToUse==null) { - myCommenterToUse = new XmlBlockCommenter(); - } - } - } - - public static final void setStyleCommenter(BlockCommenter _styleCommenter) { - ourStyleCommenter = _styleCommenter; - } - - public void commentRange(int start, int end, BlockCommenterContext context) { - initialize(null, start, end, context); - myCommenterToUse.commentRange(start, end, context); - } - - public void uncommentRange(PsiElement element, int commentStart, BlockCommenterContext context) { - initialize(element, -1, -1, context); - myCommenterToUse.uncommentRange(element, commentStart, context); - } - - public Object clone() { - try { - return super.clone(); - } - catch (CloneNotSupportedException e) { - e.printStackTrace(); - return null; - } - } - - public static void setScriptCommenter(BlockCommenter scriptCommenter) { - ourScriptCommenter = scriptCommenter; - } - } - - static class JspxBlockCommenter extends HtmlBlockCommenter { - private BlockCommenter javaCommenter = new JavaBlockCommenter(); - - private boolean isJavaCommentInsideXml(PsiElement element) { - boolean javaComment = false; - - final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class, false); - - if (tag.getName().equals("jsp:scriplet") || tag.getName().equals("jsp:declaration")) { - javaComment = true; - } - - return javaComment; - } - - public void commentRange(int start, int end, BlockCommenterContext context) { - if (isJavaCommentInsideXml( context.getFile().findElementAt(start))) { - javaCommenter.commentRange(start, end, context); - } else { - super.commentRange(start, end, context); - } - } - - public void uncommentRange(PsiElement element, int commentStart, BlockCommenterContext context) { - if (isJavaCommentInsideXml( context.getFile().findElementAt(commentStart))) { - javaCommenter.uncommentRange(element, commentStart, context); - } else { - super.uncommentRange(element, commentStart, context); - } - } - - public Object clone() { - final JspxBlockCommenter commenter = (JspxBlockCommenter)super.clone(); - commenter.javaCommenter = (BlockCommenter)javaCommenter.clone(); - return commenter; - } - } - - static class JspBlockCommenter implements BlockCommenter { - - public void commentRange(int start, int end, BlockCommenterContext context) { - PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getDocument()); - PsiElement element = context.getFile().findElementAt(start); - - if (isJavaCommentInsideJsp(element, end)) { - context.commentRange(start, end, "/*", "*/"); - } else { - context.commentRange(start, end, "<%--", "--%>"); - } - } - - public void uncommentRange(PsiElement element, int commentStart, BlockCommenterContext context) { - if (element instanceof JspToken && ((JspToken) element).getTokenType() == JspTokenType.JSP_COMMENT) { - context.uncommentRange(element.getTextRange(), "<%--", "--%>"); - } else { - if (element instanceof PsiComment && element.getText().startsWith("/*")) { - context.uncommentRange(element.getTextRange(), "/*", "*/"); - } else { - if (!isJavaCommentInsideJsp(element, commentStart)) { - context.insertEmptyComment(commentStart, "%--", "--%"); - } else { - context.insertEmptyComment(commentStart, "/*", "*/"); - } - } - } - } - - public Object clone() { - try { - return super.clone(); - } - catch (CloneNotSupportedException e) { - return null; - } - } - - private boolean isJavaCommentInsideJsp(PsiElement element, int endOffset) { - boolean javaComment = !(element instanceof JspToken); - - while (element != null && element.getTextRange().getEndOffset() <= endOffset) { - if (element instanceof JspToken) { - javaComment = false; - break; - } - element = element.getNextSibling(); - } - return javaComment; - } - } - public void invoke(Project project, Editor editor, PsiFile file) { myProject = project; myEditor = editor; @@ -282,48 +41,129 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler, B myDocument = editor.getDocument(); if (!myFile.isWritable()) { - if (!FileDocumentManager.fileForDocumentCheckedOutSuccessfully(getDocument(), project)){ + if (!FileDocumentManager.fileForDocumentCheckedOutSuccessfully(myDocument, project)){ return; } } FeatureUsageTracker.getInstance().triggerFeatureUsed("codeassists.comment.block"); - final SelectionModel selectionModel = myEditor.getSelectionModel(); - BlockCommenter commenter = getCommenter(myFile); + final Commenter commenter = getCommenter(); if (commenter == null) return; - final BlockCommenter blockCommenter = (BlockCommenter)commenter.clone(); - if (selectionModel.hasSelection()) { - int startOffset = selectionModel.getSelectionStart(); - int endOffset = selectionModel.getSelectionEnd(); + final SelectionModel selectionModel = myEditor.getSelectionModel(); - blockCommenter.commentRange(startOffset, endOffset, this); - } else { - int offset = editor.getCaretModel().getOffset(); - PsiDocumentManager.getInstance(project).commitDocument(myDocument); - PsiElement element = file.findElementAt(offset); - if (element == null && offset > 0) { - element = file.findElementAt(offset - 1); + final String prefix = commenter.getBlockCommentPrefix(); + final String suffix = commenter.getBlockCommentSuffix(); + if (prefix == null || suffix == null) return; + + TextRange commentedRange = findCommentedRange(commenter); + if (commentedRange != null) { + uncommentRange(commentedRange, prefix, suffix); + } + else { + if (selectionModel.hasBlockSelection()) { + final LogicalPosition start = selectionModel.getBlockStart(); + final LogicalPosition end = selectionModel.getBlockEnd(); + int startColumn = Math.min(start.column, end.column); + int endColumn = Math.max(start.column, end.column); + int startLine = Math.min(start.line, end.line); + int endLine = Math.max(start.line, end.line); + + for (int i = startLine; i <= endLine; i++) { + editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(i, endColumn)); + EditorModificationUtil.insertStringAtCaret(editor, suffix, true, true); + } + + for (int i = startLine; i <= endLine; i++) { + editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(i, startColumn)); + EditorModificationUtil.insertStringAtCaret(editor, prefix, true, true); + } + } + else if (selectionModel.hasSelection()) { + int selectionStart = selectionModel.getSelectionStart(); + int selectionEnd = selectionModel.getSelectionEnd(); + commentRange(selectionStart, selectionEnd, prefix, suffix); + } + else { + final LogicalPosition caretPosition = myEditor.getCaretModel().getLogicalPosition(); + EditorUtil.fillVirtualSpaceUntil(editor, caretPosition.column, caretPosition.line); + int caretOffset = myEditor.getCaretModel().getOffset(); + myDocument.insertString(caretOffset, prefix + suffix); + myEditor.getCaretModel().moveToOffset(caretOffset + prefix.length()); } - if (element == null) return; - int commentStart = (element instanceof PsiWhiteSpace) ? offset : element.getTextRange().getStartOffset(); - blockCommenter.uncommentRange(element, commentStart, this); } } - private static boolean isJavaFile(PsiFile file) { - return file instanceof PsiJavaFile || file instanceof PsiCodeFragment; + private TextRange findCommentedRange(final Commenter commenter) { + final FileType fileType = myFile.getFileType(); + if (fileType instanceof CustomFileType) { + Lexer lexer = new CustomFileTypeLexer(((CustomFileType)fileType).getSyntaxTable()); + final CharSequence text = myDocument.getCharsSequence(); + int commentStart = CharArrayUtil.lastIndexOf(text, commenter.getBlockCommentPrefix(), myEditor.getCaretModel().getOffset()); + if (commentStart == -1) return null; + char[] chars = CharArrayUtil.fromSequence(text); + lexer.start(chars, commentStart, text.length()); + + if (lexer.getTokenType() == CustomHighlighterTokenType.MULTI_LINE_COMMENT) { + return new TextRange(commentStart, lexer.getTokenEnd()); + } + return null; + } + + TextRange commentedRange = null; + PsiElement comment = findCommentAtCaret(commenter); + if (comment != null) { + String commentText = comment.getText(); + String prefix = commenter.getBlockCommentPrefix(); + String suffix = commenter.getBlockCommentSuffix(); + if (!commentText.startsWith(prefix) || !commentText.endsWith(suffix)) return null; + commentedRange = comment.getTextRange(); + } + return commentedRange; + } + + private Commenter getCommenter() { + final FileType fileType = myFile.getFileType(); + if (fileType instanceof CustomFileType) { + return ((CustomFileType)fileType).getCommenter(); + } + + final SelectionModel selectionModel = myEditor.getSelectionModel(); + int caretOffset = myEditor.getCaretModel().getOffset(); + Language lang = getLanguageAtOffset(caretOffset); + if (lang == null) return null; + if (selectionModel.hasSelection()) { + Language l1 = getLanguageAtOffset(selectionModel.getSelectionStart()); + Language l2 = getLanguageAtOffset(selectionModel.getSelectionEnd()); + if (!Comparing.equal(lang, l1) || !Comparing.equal(lang, l2)) { + // selection covers multiple languages use language of the file to comment + lang = myFile.getLanguage(); + } + } + + return lang.getCommenter(); + } + + private PsiElement findCommentAtCaret(Commenter commenter) { + PsiElement elt = myFile.findElementAt(myEditor.getCaretModel().getOffset()); + if (elt == null) return null; + final ASTNode node = elt.getNode(); + if (node != null && commenter.getBlockCommentToken() == node.getElementType()) return elt; + return PsiTreeUtil.getParentOfType(elt, PsiComment.class, false); + } + + private Language getLanguageAtOffset(final int offset) { + PsiElement elt = myFile.findElementAt(offset); + if (elt == null) { + if (offset > 0) elt = myFile.findElementAt(offset-1); + if (elt == null) return null; + } + return elt.getLanguage(); } public boolean startInWriteAction() { return true; } - public void insertEmptyComment(int offset, String commentStart, String commentEnd) { - myEditor.getDocument().insertString(offset, commentStart + commentEnd); - myEditor.getCaretModel().moveToOffset(offset + commentStart.length()); - } - - public void commentRange(int startOffset, int endOffset, String commentPrefix, String commentSuffix) { CharSequence chars = myDocument.getCharsSequence(); LogicalPosition caretPosition = myEditor.getCaretModel().getLogicalPosition(); @@ -363,22 +203,6 @@ public class CommentByBlockCommentHandler implements CodeInsightActionHandler, B myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } - public PsiFile getFile() { - return myFile; - } - - public Document getDocument() { - return myDocument; - } - - public Project getProject() { - return myProject; - } - - public Editor getEditor() { - return myEditor; - } - public void uncommentRange(TextRange range, String commentPrefix, String commentSuffix) { CharSequence chars = myDocument.getCharsSequence(); int startOffset = range.getStartOffset(); diff --git a/source/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java b/source/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java index 7c8a6ab6cf91..dc3fd252abcf 100644 --- a/source/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java +++ b/source/com/intellij/codeInsight/generation/CommentByLineCommentHandler.java @@ -3,35 +3,25 @@ package com.intellij.codeInsight.generation; import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.featureStatistics.FeatureUsageTracker; +import com.intellij.ide.highlighter.custom.impl.CustomFileType; +import com.intellij.lang.Commenter; +import com.intellij.lang.Language; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.FoldRegion; import com.intellij.openapi.editor.SelectionModel; -import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.impl.FoldingModelImpl; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.StdFileTypes; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.psi.*; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.codeStyle.Indent; -import com.intellij.psi.jsp.JspFile; -import com.intellij.psi.jsp.JspToken; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.psi.xml.XmlFile; -import com.intellij.psi.xml.XmlTag; -import com.intellij.psi.xml.XmlToken; -import com.intellij.psi.xml.XmlTokenType; import com.intellij.util.text.CharArrayUtil; -import java.util.HashMap; -import java.util.Map; - -public class CommentByLineCommentHandler implements CodeInsightActionHandler, LineCommenter.LineCommenterContext { +public class CommentByLineCommentHandler implements CodeInsightActionHandler { private Project myProject; private PsiFile myFile; private Editor myEditor; @@ -42,48 +32,10 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler, Li private int myLine2; private int[] myStartOffsets; private int[] myEndOffsets; + private Commenter[] myCommenters; private boolean myAllLineComments; private CodeStyleManager myCodeStyleManager; - private static final Map customCommenters = new HashMap(4); - - public static final void registerCommenter(FileType fileType, LineCommenter commenter) { - customCommenters.put(fileType, commenter); - } - - static { - registerCommenter(StdFileTypes.XML,new XmlLineCommenter()); - HtmlLineCommenter commenter = new HtmlLineCommenter(); - registerCommenter(StdFileTypes.HTML,commenter); - registerCommenter(StdFileTypes.XHTML,commenter); - registerCommenter(StdFileTypes.JAVA, new JavaLineCommenter()); - registerCommenter(StdFileTypes.JSP, new JspLineCommenter()); - - registerCommenter(StdFileTypes.JSPX, new JspxLineCommenter()); - } - - public static LineCommenter getCustomCommenter(FileType fileType) { - return customCommenters.get(fileType); - } - - public static LineCommenter getCommenter(PsiFile file) { - LineCommenter customCommenter = customCommenters.get(file.getFileType()); - - if (customCommenter!=null) { - return customCommenter; - } else if (isJavaFile(file)) { - return new JavaLineCommenter(); - } - else if (file instanceof XmlFile) { - return new XmlLineCommenter(); - } - else if (file instanceof JspFile) { - return new JspLineCommenter(); - } else { - return null; - } - } - public void invoke(Project project, Editor editor, PsiFile file) { myProject = project; myFile = file; @@ -91,7 +43,7 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler, Li myDocument = myEditor.getDocument(); if (!myFile.isWritable()) { - if (!FileDocumentManager.fileForDocumentCheckedOutSuccessfully(getDocument(), project)){ + if (!FileDocumentManager.fileForDocumentCheckedOutSuccessfully(myDocument, project)){ return; } } @@ -145,49 +97,85 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler, Li myStartOffsets = new int[myLine2 - myLine1 + 1]; myEndOffsets = new int[myLine2 - myLine1 + 1]; + myCommenters = new Commenter[myLine2 - myLine1 + 1]; myAllLineComments = true; CharSequence chars = myDocument.getCharsSequence(); - LineCommenter lineCommenter = customCommenters.get(myFile.getFileType()); - if (lineCommenter==null) return; - LineCommenter customCommenter = (LineCommenter)lineCommenter.clone(); for (int line = myLine1; line <= myLine2; line++) { - int offset1 = getCommentStart(line,customCommenter); - myStartOffsets[line - myLine1] = offset1; - if (offset1 == -1) { - myAllLineComments = false; - break; - } - - int offset = myDocument.getLineEndOffset(line) + ((DocumentEx) myDocument).getLineSeparatorLength(line); - offset = CharArrayUtil.shiftBackward(chars, offset - 1, "\n\r") + 1; - - int offset2 = customCommenter.getCommentEnd(offset,this); - myEndOffsets[line - myLine1] = offset2; - - if (offset2 == -1) { + final Commenter commenter = findCommenter(line); + if (commenter == null) return; + if (commenter.getLineCommentPrefix() == null && (commenter.getBlockCommentPrefix() == null || commenter.getBlockCommentSuffix() == null)) return; + myCommenters[line - myLine1] = commenter; + if (!isLineCommented(line, chars, commenter)) { myAllLineComments = false; break; } } if (!myAllLineComments) { - new CommentPerformer(customCommenter).perform(); + new CommentPerformer().perform(); } else { for (int line = myLine2; line >= myLine1; line--) { int offset1 = myStartOffsets[line - myLine1]; int offset2 = myEndOffsets[line - myLine1]; - customCommenter.doUncomment(offset1,offset2,this); + Commenter commenter = myCommenters[line - myLine1]; + if (offset2 >= 0) { + myDocument.deleteString(offset2 - commenter.getBlockCommentSuffix().length(), offset2); + } + if (offset1 >= 0) { + myDocument.deleteString(offset1, offset1 + commenter.getBlockCommentPrefix().length()); + } } } } - private Indent computeMinIndent(int line1, int line2, CharSequence chars, CodeStyleManager codeStyleManager, FileType fileType, LineCommenter commenter) { + private boolean isLineCommented(final int line, final CharSequence chars, final Commenter commenter) { + boolean commented; + String prefix = commenter.getLineCommentPrefix(); + int lineStart = myDocument.getLineStartOffset(line); + lineStart = CharArrayUtil.shiftForward(chars, lineStart, " \t"); + if (prefix != null) { + commented = CharArrayUtil.regionMatches(chars, lineStart, prefix); + if (commented) { + myStartOffsets[line - myLine1] = lineStart; + myEndOffsets[line - myLine1] = -1; + } + } + else { + prefix = commenter.getBlockCommentPrefix(); + String suffix = commenter.getBlockCommentSuffix(); + int lineEnd = myDocument.getLineEndOffset(line); + lineEnd = CharArrayUtil.shiftBackward(chars, lineEnd, " \t"); + commented = CharArrayUtil.regionMatches(chars, lineStart, prefix) && CharArrayUtil.regionMatches(chars, lineEnd - suffix.length(), suffix); + if (commented) { + myStartOffsets[line - myLine1] = lineStart; + myEndOffsets[line - myLine1] = lineEnd; + } + + } + return commented; + } + + private Commenter findCommenter(final int line) { + final FileType fileType = myFile.getFileType(); + if (fileType instanceof CustomFileType) { + return ((CustomFileType)fileType).getCommenter(); + } + + int offset = myDocument.getLineStartOffset(line); + final PsiElement elt = myFile.findElementAt(offset); + if (elt == null) return null; + final Language lang = elt.getLanguage(); + if (lang == null) return null; + return lang.getCommenter(); + } + + private Indent computeMinIndent(int line1, int line2, CharSequence chars, CodeStyleManager codeStyleManager, FileType fileType) { Indent minIndent = CodeInsightUtil.getMinLineIndent(myProject, myDocument, line1, line2, fileType); if (line1 > 0) { - int commentOffset = getCommentStart(line1 - 1,commenter); + int commentOffset = getCommentStart(line1 - 1); if (commentOffset >= 0) { int lineStart = myDocument.getLineStartOffset(line1 - 1); String space = chars.subSequence(lineStart, commentOffset).toString(); @@ -201,86 +189,70 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler, Li return minIndent; } - - private int getCommentStart(int line, LineCommenter commenter ) { + private int getCommentStart(int line) { int offset = myDocument.getLineStartOffset(line); CharSequence chars = myDocument.getCharsSequence(); offset = CharArrayUtil.shiftForward(chars, offset, " \t"); - - return commenter.getCommentStart(offset,this); - } - - private static boolean isJavaFile(PsiFile file) { - return file instanceof PsiJavaFile || file instanceof PsiCodeFragment; - } - - public CharSequence getChars() { - return myDocument.getCharsSequence(); - } - - public Document getDocument() { - return myDocument; - } - - public Project getProject() { - return myProject; - } - - public PsiFile getFile() { - return myFile; - } - - public int getStartLine() { - return myLine1; - } - - public int getEndLine() { - return myLine2; + final Commenter commenter = findCommenter(line); + if (commenter == null) return -1; + String prefix = commenter.getLineCommentPrefix(); + if (prefix == null) prefix = commenter.getBlockCommentPrefix(); + if (prefix == null) return -1; + return CharArrayUtil.regionMatches(chars, offset, prefix) ? offset : -1; } private class CommentPerformer { - LineCommenter myCommenter; - - public CommentPerformer(LineCommenter commenter) { - myCommenter = commenter; + public CommentPerformer() { } public void perform() { if (CodeStyleSettingsManager.getSettings(myProject).LINE_COMMENT_AT_FIRST_COLUMN) { - new DefaultCommentPerformer(myCommenter).perform(); + new DefaultCommentPerformer().perform(); } else { - new IndentCommentPerformer(myCommenter).perform(); + new IndentCommentPerformer().perform(); } } } private class DefaultCommentPerformer { - LineCommenter myCommenter; - - public DefaultCommentPerformer(LineCommenter commenter) { - myCommenter = commenter; + public DefaultCommentPerformer() { } public void perform() { for (int line = myLine2; line >= myLine1; line--) { int offset = myDocument.getLineStartOffset(line); - myCommenter.doComment(offset, line, CommentByLineCommentHandler.this); + commentLine(line, offset); } } } - private class IndentCommentPerformer { - LineCommenter myCommenter; + private void commentLine(int line, int offset) { + final Commenter commenter = findCommenter(line); + String prefix = commenter.getLineCommentPrefix(); + if (prefix != null) { + myDocument.insertString(offset, prefix); + } + else { + prefix = commenter.getBlockCommentPrefix(); + String suffix = commenter.getBlockCommentSuffix(); + int endOffset = myDocument.getLineEndOffset(line); + offset = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), offset, " \t"); + endOffset = CharArrayUtil.shiftBackward(myDocument.getCharsSequence(), endOffset, " \t"); + myDocument.insertString(endOffset, suffix); + myDocument.insertString(offset, prefix); + } + } - public IndentCommentPerformer(LineCommenter commenter) { - myCommenter = commenter; + private class IndentCommentPerformer { + + public IndentCommentPerformer() { } public void perform() { CharSequence chars = myDocument.getCharsSequence(); final FileType fileType = myFile.getFileType(); - Indent minIndent = computeMinIndent(myLine1, myLine2, chars, myCodeStyleManager, fileType,myCommenter); + Indent minIndent = computeMinIndent(myLine1, myLine2, chars, myCodeStyleManager, fileType); for (int line = myLine2; line >= myLine1; line--) { int lineStart = myDocument.getLineStartOffset(line); @@ -301,329 +273,8 @@ public class CommentByLineCommentHandler implements CodeInsightActionHandler, Li offset++; } - myCommenter.doComment(offset, line, CommentByLineCommentHandler.this); + commentLine(line, offset); } } } - - public static class HtmlLineCommenter implements LineCommenter { - private static LineCommenter ourStyleCommenter; - private boolean myInitialized; - private LineCommenter myCommenterToUse; - private static LineCommenter ourScriptCommenter; - - private void initialize(LineCommenterContext context) { - if (!myInitialized) { - PsiElement elementInclusiveRange = PsiUtil.getElementInclusiveRange( - context.getFile(), - new TextRange( - context.getDocument().getLineStartOffset(context.getStartLine()), - context.getDocument().getLineEndOffset(context.getEndLine()) - ) - ); - elementInclusiveRange = PsiTreeUtil.getParentOfType(elementInclusiveRange,XmlTag.class,false); - - if (elementInclusiveRange instanceof XmlTag) { - String tagName = ((XmlTag)elementInclusiveRange).getName(); - - if (tagName.equalsIgnoreCase("style") && - ourStyleCommenter!=null - ) { - myCommenterToUse = (LineCommenter)ourStyleCommenter.clone(); - } else if (tagName.equalsIgnoreCase("script") && - ourScriptCommenter!=null - ) { - myCommenterToUse = (LineCommenter)ourScriptCommenter.clone(); - } - } - - if (myCommenterToUse == null) { - myCommenterToUse = new XmlLineCommenter(); - } - myInitialized = true; - } - } - - public static final void setStyleCommenter(LineCommenter _styleCommenter) { - ourStyleCommenter = _styleCommenter; - } - - public void doComment(int offset, int line, LineCommenterContext context) { - initialize(context); - myCommenterToUse.doComment(offset, line, context); - } - - public int getCommentEnd(int offset, LineCommenterContext context) { - initialize(context); - return myCommenterToUse.getCommentEnd(offset, context); - } - - public int getCommentStart(int offset, LineCommenterContext context) { - initialize(context); - return myCommenterToUse.getCommentStart(offset, context); - } - - public void doUncomment(int offset1, int offset2, LineCommenterContext context) { - initialize(context); - myCommenterToUse.doUncomment(offset1, offset2, context); - } - - public Object clone() { - try { - return super.clone(); - } - catch (CloneNotSupportedException e) { - e.printStackTrace(); - return null; - } - } - - public static void setScriptCommenter(LineCommenter scriptCommenter) { - ourScriptCommenter = scriptCommenter; - } - } - - private static class XmlLineCommenter implements LineCommenter { - public void doComment(int offset, int line, LineCommenterContext context) { - final Document myDocument = context.getDocument(); - myDocument.insertString(offset, ""); - } - - public int getCommentEnd(int offset, LineCommenterContext context) { - offset -= "-->".length(); - if (offset < 0) return -1; - - if (!CharArrayUtil.regionMatches(context.getChars(), offset, "-->")) return -1; - - PsiDocumentManager.getInstance(context.getProject()).commitDocument(context.getDocument()); - PsiElement element = context.getFile().findElementAt(offset); - if (element instanceof XmlToken && element.getTextRange().getStartOffset() == offset) { - XmlToken token = (XmlToken)element; - - if (token.getTokenType() == XmlTokenType.XML_COMMENT_END) { - - return offset; - } - } - - return -1; - } - - public int getCommentStart(int offset, LineCommenterContext context) { - if (offset > context.getDocument().getTextLength() - ""; + } +} diff --git a/source/com/intellij/psi/impl/compiled/ClsDocCommentImpl.java b/source/com/intellij/psi/impl/compiled/ClsDocCommentImpl.java index 245026d5dc2e..62c48edfa380 100644 --- a/source/com/intellij/psi/impl/compiled/ClsDocCommentImpl.java +++ b/source/com/intellij/psi/impl/compiled/ClsDocCommentImpl.java @@ -4,13 +4,14 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.JavaTokenType; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.tree.IElementType; +import com.intellij.psi.PsiJavaToken; import com.intellij.psi.impl.source.tree.ElementType; import com.intellij.psi.impl.source.tree.TreeElement; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocTag; +import com.intellij.psi.tree.IElementType; -class ClsDocCommentImpl extends ClsElementImpl implements PsiDocComment, JavaTokenType{ +class ClsDocCommentImpl extends ClsElementImpl implements PsiDocComment, JavaTokenType, PsiJavaToken { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.ClsDocCommentImpl"); private final ClsElementImpl myParent; diff --git a/source/com/intellij/psi/impl/source/javadoc/PsiDocCommentImpl.java b/source/com/intellij/psi/impl/source/javadoc/PsiDocCommentImpl.java index 1bf4f25675bb..d581be1854da 100644 --- a/source/com/intellij/psi/impl/source/javadoc/PsiDocCommentImpl.java +++ b/source/com/intellij/psi/impl/source/javadoc/PsiDocCommentImpl.java @@ -1,5 +1,6 @@ package com.intellij.psi.impl.source.javadoc; +import com.intellij.lang.ASTNode; import com.intellij.lexer.JavaDocLexer; import com.intellij.lexer.JavaLexer; import com.intellij.openapi.diagnostic.Logger; @@ -7,6 +8,7 @@ import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.JavaTokenType; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiJavaToken; import com.intellij.psi.impl.source.SourceTreeToPsiMap; import com.intellij.psi.impl.source.parsing.ChameleonTransforming; import com.intellij.psi.impl.source.tree.*; @@ -16,12 +18,11 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.util.CharTable; import com.intellij.util.text.CharArrayCharSequence; -import com.intellij.lang.ASTNode; import java.util.ArrayList; import java.util.regex.Pattern; -public class PsiDocCommentImpl extends CompositePsiElement implements PsiDocComment, JavaTokenType, Reparseable { +public class PsiDocCommentImpl extends CompositePsiElement implements PsiDocComment, JavaTokenType, Reparseable, PsiJavaToken { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.javadoc.PsiDocCommentImpl"); private static final TokenSet TAG_BIT_SET = TokenSet.create(new IElementType[]{DOC_TAG}); diff --git a/source/com/intellij/psi/impl/source/tree/PsiCommentImpl.java b/source/com/intellij/psi/impl/source/tree/PsiCommentImpl.java index 180153cc9af3..6ea6d12e0613 100644 --- a/source/com/intellij/psi/impl/source/tree/PsiCommentImpl.java +++ b/source/com/intellij/psi/impl/source/tree/PsiCommentImpl.java @@ -1,13 +1,12 @@ package com.intellij.psi.impl.source.tree; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiComment; -import com.intellij.psi.PsiElementVisitor; +import com.intellij.lang.Language; +import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.util.CharTable; -public class PsiCommentImpl extends LeafPsiElement implements PsiComment, JavaTokenType { - protected PsiCommentImpl(IElementType type, char[] buffer, int startOffset, int endOffset, int lexerState, CharTable table) { +public class PsiCommentImpl extends LeafPsiElement implements PsiComment, JavaTokenType, PsiJavaToken { + public PsiCommentImpl(IElementType type, char[] buffer, int startOffset, int endOffset, int lexerState, CharTable table) { super(type, buffer, startOffset, endOffset, lexerState, table); } @@ -22,4 +21,11 @@ public class PsiCommentImpl extends LeafPsiElement implements PsiComment, JavaTo public String toString(){ return "PsiComment(" + getElementType().toString() + ")"; } + + public Language getLanguage() { + PsiElement master = getNextSibling(); + if (master == null) master = getPrevSibling(); + if (master == null) master = getParent(); + return master.getLanguage(); + } } diff --git a/source/com/intellij/psi/impl/source/tree/PsiWhiteSpaceImpl.java b/source/com/intellij/psi/impl/source/tree/PsiWhiteSpaceImpl.java index c2234b44e871..b8e848d795f4 100644 --- a/source/com/intellij/psi/impl/source/tree/PsiWhiteSpaceImpl.java +++ b/source/com/intellij/psi/impl/source/tree/PsiWhiteSpaceImpl.java @@ -1,12 +1,14 @@ package com.intellij.psi.impl.source.tree; +import com.intellij.lang.Language; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.TokenType; import com.intellij.util.CharTable; public class PsiWhiteSpaceImpl extends LeafPsiElement implements PsiWhiteSpace { - protected PsiWhiteSpaceImpl(char[] buffer, int startOffset, int endOffset, int lexerState, CharTable table) { + public PsiWhiteSpaceImpl(char[] buffer, int startOffset, int endOffset, int lexerState, CharTable table) { super(TokenType.WHITE_SPACE, buffer, startOffset, endOffset, lexerState, table); } @@ -17,4 +19,11 @@ public class PsiWhiteSpaceImpl extends LeafPsiElement implements PsiWhiteSpace { public String toString(){ return "PsiWhiteSpace"; } + + public Language getLanguage() { + PsiElement master = getNextSibling(); + if (master == null) master = getPrevSibling(); + if (master == null) master = getParent(); + return master.getLanguage(); + } } diff --git a/source/com/intellij/psi/impl/source/xml/XmlCommentImpl.java b/source/com/intellij/psi/impl/source/xml/XmlCommentImpl.java index 93ecf357306b..5ff37c7f506d 100644 --- a/source/com/intellij/psi/impl/source/xml/XmlCommentImpl.java +++ b/source/com/intellij/psi/impl/source/xml/XmlCommentImpl.java @@ -1,6 +1,7 @@ package com.intellij.psi.impl.source.xml; import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.tree.IElementType; import com.intellij.psi.xml.XmlComment; /** @@ -11,6 +12,10 @@ public class XmlCommentImpl extends XmlElementImpl implements XmlComment { super(XML_COMMENT); } + public IElementType getTokenType() { + return XML_COMMENT; + } + public void accept(PsiElementVisitor visitor) { visitor.visitXmlComment(this); }