| /******************************************************************************* |
| * Copyright (c) 2011 NumberFour AG |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * NumberFour AG - initial API and Implementation (Alex Panchenko) |
| *******************************************************************************/ |
| package org.eclipse.dltk.javascript.internal.ui.text; |
| |
| import static org.eclipse.dltk.javascript.ast.MultiLineComment.JSDOC_PREFIX; |
| import static org.eclipse.dltk.javascript.internal.ui.text.JavascriptAutoEditStrategy.C_END; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.dltk.core.IMethod; |
| import org.eclipse.dltk.javascript.internal.corext.codemanipulation.JSCodeGeneration; |
| import org.eclipse.dltk.javascript.ui.text.IJavaScriptPartitions; |
| import org.eclipse.dltk.ui.text.ScriptDefaultIndentLineAutoEditStrategy; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.DocumentCommand; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.ITypedRegion; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.jface.text.TextUtilities; |
| |
| public class JSDocAutoIndentStrategy extends |
| ScriptDefaultIndentLineAutoEditStrategy { |
| |
| public JSDocAutoIndentStrategy(IPreferenceStore fPreferenceStore) { |
| super(fPreferenceStore); |
| } |
| |
| @Override |
| public void customizeDocumentCommand(IDocument document, |
| DocumentCommand command) { |
| if (command.doit == false) { |
| return; |
| } |
| // TODO check smart mode |
| if (command.length == 0 && command.text != null) { |
| if (isLineDelimiter(document, command.text)) { |
| indentAfterNewLine(document, command); |
| } else if (command.text.equals("/")) { //$NON-NLS-1$ |
| indentAfterCommentEnd(document, command); |
| } else if (command.text.equals("*")) { |
| JavascriptAutoEditStrategy.smartCloseJSDoc(document, command); |
| } |
| } |
| } |
| |
| private boolean isCloseDocs() { |
| // TODO introduce preference |
| return true; |
| } |
| |
| private void indentAfterNewLine(IDocument d, DocumentCommand c) { |
| int offset = c.offset; |
| if (offset == -1 || d.getLength() == 0) |
| return; |
| try { |
| int p = (offset == d.getLength() ? offset - 1 : offset); |
| IRegion line = d.getLineInformationOfOffset(p); |
| |
| int lineOffset = line.getOffset(); |
| int firstNonWS = findEndOfWhiteSpace(d, lineOffset, offset); |
| Assert.isTrue(firstNonWS >= lineOffset, |
| "indentation must not be negative"); //$NON-NLS-1$ |
| |
| StringBuilder buf = new StringBuilder(c.text); |
| IRegion prefix = findPrefixRange(d, line); |
| String indentation = d.get(prefix.getOffset(), prefix.getLength()); |
| int lengthToAdd = Math.min(offset - prefix.getOffset(), |
| prefix.getLength()); |
| |
| buf.append(indentation.substring(0, lengthToAdd)); |
| |
| if (firstNonWS < offset) { |
| if (d.getChar(firstNonWS) == '/') { |
| // Javadoc started on this line |
| buf.append(" * "); //$NON-NLS-1$ |
| |
| if (isCloseDocs() && isNewComment(d, offset)) { |
| c.shiftsCaret = false; |
| c.caretOffset = c.offset + buf.length(); |
| String lineDelimiter = TextUtilities |
| .getDefaultLineDelimiter(d); |
| |
| int eolOffset = lineOffset + line.getLength(); |
| int replacementLength = eolOffset - p; |
| String restOfLine = d.get(p, replacementLength); |
| String endTag = lineDelimiter + indentation + " */"; //$NON-NLS-1$ |
| if (isGenerateStub()) { |
| d.replace(offset, replacementLength, endTag); |
| final IMethod method = JavascriptAutoEditStrategy |
| .findMethod(d, offset - replacementLength |
| + endTag.length(), true); |
| if (method != null) { |
| buf.append(restOfLine); |
| String string = JSCodeGeneration |
| .getMethodComment(method, null, |
| lineDelimiter); |
| if (string != null) { |
| string = normalizeGeneratedDoc(string); |
| string = JSCodeGeneration.changeIndent( |
| string, 0, |
| method.getScriptProject(), |
| indentation, lineDelimiter); |
| /* |
| * only add tags if they are non-empty - the |
| * empty line has already been added above. |
| */ |
| if (string.length() != 0)//$NON-NLS-1$ |
| buf.append(string); |
| } |
| } |
| } else { |
| c.length = replacementLength; |
| buf.append(restOfLine); |
| buf.append(endTag); |
| } |
| } |
| } |
| } |
| // move the caret behind the prefix, even if we do not have to |
| // insert it. |
| if (lengthToAdd < prefix.getLength()) |
| c.caretOffset = offset + prefix.getLength() - lengthToAdd; |
| c.text = buf.toString(); |
| } catch (BadLocationException excp) { |
| // stop work |
| } |
| } |
| |
| static String normalizeGeneratedDoc(String string) { |
| string = string.trim(); |
| if (string.startsWith(JSDOC_PREFIX)) { |
| string = string.substring(JSDOC_PREFIX.length()); |
| } |
| if (string.endsWith(C_END)) { |
| string = string.substring(0, string.length() - C_END.length()); |
| } |
| string = string.trim(); |
| if (string.startsWith("*")) { |
| string = string.substring(1).trim(); |
| } |
| return string; |
| } |
| |
| static boolean isGenerateStub() { |
| return true; |
| } |
| |
| private IRegion findPrefixRange(IDocument document, IRegion line) |
| throws BadLocationException { |
| int lineOffset = line.getOffset(); |
| int lineEnd = lineOffset + line.getLength(); |
| int indentEnd = findEndOfWhiteSpace(document, lineOffset, lineEnd); |
| if (indentEnd < lineEnd && document.getChar(indentEnd) == '*') { |
| indentEnd++; |
| while (indentEnd < lineEnd && document.getChar(indentEnd) == ' ') |
| indentEnd++; |
| } |
| return new Region(lineOffset, indentEnd - lineOffset); |
| } |
| |
| private boolean isNewComment(IDocument document, int commandOffset) { |
| try { |
| int lineIndex = document.getLineOfOffset(commandOffset) + 1; |
| if (lineIndex >= document.getNumberOfLines()) |
| return true; |
| IRegion line = document.getLineInformation(lineIndex); |
| ITypedRegion partition = TextUtilities |
| .getPartition(document, |
| IJavaScriptPartitions.JS_PARTITIONING, |
| commandOffset, false); |
| int partitionEnd = partition.getOffset() + partition.getLength(); |
| if (line.getOffset() >= partitionEnd) |
| return false; |
| if (document.getLength() == partitionEnd) |
| return true; // partition goes to end of document - probably a |
| // new comment |
| String comment = document.get(partition.getOffset(), |
| partition.getLength()); |
| if (comment.indexOf("/*", 2) != -1) //$NON-NLS-1$ |
| return true; // enclosed another comment -> probably a new |
| // comment |
| return false; |
| } catch (BadLocationException e) { |
| return false; |
| } |
| } |
| |
| private void indentAfterCommentEnd(IDocument document, |
| DocumentCommand command) { |
| if (command.offset < 2 || document.getLength() == 0) { |
| return; |
| } |
| try { |
| if ("* ".equals(document.get(command.offset - 2, 2))) { //$NON-NLS-1$ |
| // modify document command |
| command.length++; |
| command.offset--; |
| } |
| } catch (BadLocationException excp) { |
| // stop work |
| } |
| } |
| |
| } |