/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.javadoc.PsiDocParamRef;
import com.intellij.psi.impl.source.tree.JavaDocElementType;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.javadoc.PsiDocTagValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Advises typing in javadoc if necessary.
*
* @author Denis Zhdanov
* @since 2/2/11 11:17 AM
*/
public class JavadocTypedHandler extends TypedHandlerDelegate {
private static final char START_TAG_SYMBOL = '<';
private static final char CLOSE_TAG_SYMBOL = '>';
private static final char SLASH = '/';
@Override
public Result charTyped(char c, Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (project == null || editor == null || file == null) {
return Result.CONTINUE;
}
insertClosingTagIfNecessary(c, project, editor, file);
return Result.CONTINUE;
}
/**
* Checks if it's necessary to insert closing tag on typed character.
*
* @param c typed symbol
* @param project current project
* @param editor current editor
* @param file current file
* @return true
if closing tag is inserted; false
otherwise
*/
private static boolean insertClosingTagIfNecessary(char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (c != CLOSE_TAG_SYMBOL || !CodeInsightSettings.getInstance().JAVADOC_GENERATE_CLOSING_TAG) {
return false;
}
PsiDocumentManager.getInstance(project).commitAllDocuments();
if (!isAppropriatePlace(editor, file)) {
return false;
}
// Inspect symbols to the left of the current caret position, insert closing tag only if valid tag is just typed
// (e.g. don't insert anything on single '>' symbol typing).
int offset = editor.getCaretModel().getOffset();
Document document = editor.getDocument();
CharSequence tagName = getTagName(document.getText(), offset);
if (tagName == null) {
return false;
}
document.insertString(offset, String.valueOf(START_TAG_SYMBOL) + SLASH + tagName + CLOSE_TAG_SYMBOL);
return true;
}
/**
* Tries to derive start tag name assuming that given offset points to position just after '>'
symbol.
*
null
when offset is not located just after start tag, e.g. the following situations:
* *
null
otherwise
*/
@Nullable
static CharSequence getTagName(@NotNull CharSequence text, int afterTagOffset) {
if (afterTagOffset > text.length()) {
return null;
}
int endOffset = afterTagOffset - 1;
// Check empty element like
if (endOffset > 0 && text.charAt(endOffset - 1) == SLASH) {
return null;
}
for (int i = endOffset - 1; i >= 0; i--) {
char c = text.charAt(i);
switch (c) {
case '\n': return null;
case CLOSE_TAG_SYMBOL: return null;
case START_TAG_SYMBOL:
if (text.charAt(i + 1) == SLASH) {
// Handle situation like