--- /dev/null
+/*
+ * Copyright (c) 2003-2004 Christopher Lenz and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * Christopher Lenz - initial API and implementation
+ *
+ * $Id: CssAutoEditStrategy.java,v 1.1 2004-09-02 18:11:48 jsurfer Exp $
+ */
+
+package net.sourceforge.phpeclipse.css.ui.internal.text;
+
+import net.sourceforge.phpeclipse.css.core.internal.text.CssTextUtils;
+import net.sourceforge.phpeclipse.css.ui.CssUI;
+import net.sourceforge.phpeclipse.css.ui.internal.CssUIPreferences;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DefaultAutoIndentStrategy;
+import org.eclipse.jface.text.DefaultLineTracker;
+import org.eclipse.jface.text.DocumentCommand;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ILineTracker;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.texteditor.ITextEditorExtension3;
+import org.eclipse.ui.texteditor.ITextEditorExtension3.InsertMode;
+
+/**
+ * Implements various auto-editing aspects for editing CSS:
+ * <ul>
+ * <li>
+ * Smart indenting after opening or closing a block
+ * </li>
+ * <li>
+ * Smart insertion of closing braces
+ * </li>
+ * </ul>
+ * All these only operate when 'Smart Insert' mode is enabled, and the closing
+ * of braces, brackets, parenthesis and strings can be disabled in the editor
+ * preferences.
+ */
+public class CssAutoEditStrategy extends DefaultAutoIndentStrategy {
+
+ // Constants ---------------------------------------------------------------
+
+ /**
+ * Alias for the preference constant <code>EDITOR_SPACES_FOR_TABS</code>.
+ */
+ public static final String SPACES_FOR_TABS =
+ CssUIPreferences.EDITOR_SPACES_FOR_TABS;
+
+ /**
+ * Alias for the preference constant <code>EDITOR_TAB_WIDTH</code>.
+ */
+ public static final String TAB_WIDTH =
+ CssUIPreferences.EDITOR_TAB_WIDTH;
+
+ // Instance Variables ------------------------------------------------------
+
+ /**
+ * The line tracker.
+ */
+ private ILineTracker lineTracker;
+
+ /**
+ * The preference store.
+ */
+ private IPreferenceStore store;
+
+ // Constructors ------------------------------------------------------------
+
+ /**
+ * Default constructor.
+ */
+ public CssAutoEditStrategy() {
+ lineTracker = new DefaultLineTracker();
+ store = CssUI.getDefault().getPreferenceStore();
+ }
+
+ // DefaultAutoIndentStrategy Implementation --------------------------------
+
+ /**
+ * @see org.eclipse.jface.text.IAutoEditStrategy#customizeDocumentCommand(IDocument, DocumentCommand)
+ */
+ public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
+ if (isSmartInsertMode()) {
+ if (isNewLine(d, c)) {
+ smartIndentAfterNewLine(d, c);
+ } else if (c.text != null) {
+ if (c.text.length() == 1) {
+ switch (c.text.charAt(0)) {
+ case '}': {
+ smartInsertClosingBrace(d, c);
+ break;
+ }
+ default: {
+ // do nothing
+ }
+ }
+ }
+ if (store.getBoolean(SPACES_FOR_TABS)) {
+ convertTabs(d, c);
+ }
+ }
+ }
+ }
+
+ // Private Methods ---------------------------------------------------------
+
+ private void convertTabs(IDocument d, DocumentCommand c) {
+ int index = c.text.indexOf('\t');
+ if (index > -1) {
+ StringBuffer buffer = new StringBuffer();
+ lineTracker.set(c.text);
+ int lines = lineTracker.getNumberOfLines();
+ try {
+ for (int i = 0; i < lines; i++) {
+ int offset = lineTracker.getLineOffset(i);
+ int endOffset = offset + lineTracker.getLineLength(i);
+ String line = c.text.substring(offset, endOffset);
+ int position = 0;
+ if (i == 0) {
+ IRegion firstLine =
+ d.getLineInformationOfOffset(c.offset);
+ position = c.offset - firstLine.getOffset();
+ }
+ int length = line.length();
+ for (int j = 0; j < length; j++) {
+ char ch = line.charAt(j);
+ if (ch == '\t') {
+ int tabWidth = getTabWidth();
+ int remainder = position % tabWidth;
+ remainder = tabWidth - remainder;
+ for (int k = 0; k < remainder; k++) {
+ buffer.append(' ');
+ }
+ position += remainder;
+ } else {
+ buffer.append(ch);
+ position++;
+ }
+ }
+ }
+ c.text = buffer.toString();
+ } catch (BadLocationException e) {
+ // ignore
+ }
+ }
+ }
+
+ private String getIndentOfLine(IDocument d, int line)
+ throws BadLocationException {
+ if (line > -1) {
+ int start = d.getLineOffset(line);
+ int end = start + d.getLineLength(line) - 1;
+ int whiteEnd = findEndOfWhiteSpace(d, start, end);
+ return d.get(start, whiteEnd - start);
+ } else {
+ return ""; //$NON-NLS-1$
+ }
+ }
+
+ private int getTabWidth() {
+ return store.getInt(TAB_WIDTH);
+ }
+
+ private boolean isNewLine(IDocument d, DocumentCommand c) {
+ if ((c.length == 0) && (c.text != null)) {
+ String[] delimiters = d.getLegalLineDelimiters();
+ for (int i = 0; i < delimiters.length; i++) {
+ if (c.text.equals(delimiters[i])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isSmartInsertMode() {
+ IWorkbenchWindow window =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (window != null) {
+ IWorkbenchPage page = window.getActivePage();
+ if (page != null) {
+ IEditorPart part = page.getActiveEditor();
+ if (part instanceof ITextEditorExtension3) {
+ InsertMode insertMode =
+ ((ITextEditorExtension3) part).getInsertMode();
+ return (insertMode == ITextEditorExtension3.SMART_INSERT);
+ }
+ }
+ }
+ return false;
+ }
+
+ private String createIndentation(int level) {
+ StringBuffer buf = new StringBuffer();
+ if (store.getBoolean(SPACES_FOR_TABS)) {
+ int tabWidth = getTabWidth();
+ for (int i = 0; i < level * tabWidth; i++) {
+ buf.append(' ');
+ }
+ } else {
+ for (int i = 0; i < level; i++) {
+ buf.append('\t');
+ }
+ }
+ return buf.toString();
+ }
+
+ private void smartInsertClosingBrace(IDocument d, DocumentCommand c) {
+ int docLength = d.getLength();
+ if ((c.offset == -1) || (docLength == 0)) {
+ return;
+ }
+ try {
+ int lineOffset = d.getLineInformationOfOffset(c.offset).getOffset();
+ if (c.offset == findEndOfWhiteSpace(d, lineOffset, c.offset)) {
+ int openingBraceOffset =
+ CssTextUtils.findMatchingOpeningPeer(d, c.offset - 1, '}');
+ if (openingBraceOffset >= 0) {
+ int openingBraceLine =
+ d.getLineOfOffset(openingBraceOffset);
+ StringBuffer buf =
+ new StringBuffer(getIndentOfLine(d, openingBraceLine));
+ buf.append(c.text);
+ c.length += c.offset - lineOffset;
+ c.offset = lineOffset;
+ c.text = buf.toString();
+ }
+ }
+ } catch (BadLocationException e) {
+ CssUI.log(
+ "Failed to smart indent after closing brace", e); //$NON-NLS-1$
+ }
+ }
+
+ private void smartIndentAfterNewLine(IDocument d, DocumentCommand c) {
+ try {
+ int docLength = d.getLength();
+ if ((c.offset == -1) || (docLength == 0)) {
+ return;
+ }
+ StringBuffer buf = new StringBuffer(c.text);
+ if ((c.offset < docLength) && (d.getChar(c.offset - 1) == '}')) {
+ // indent with the same amount used before the block was opened
+ int openingBraceOffset =
+ CssTextUtils.findMatchingOpeningPeer(d, c.offset - 2, '}');
+ if (openingBraceOffset >= 0) {
+ int openingBraceLine =
+ d.getLineOfOffset(openingBraceOffset);
+ buf.append(getIndentOfLine(d, openingBraceLine));
+ }
+ } else if ((c.offset < docLength)
+ && (d.getChar(c.offset - 1) == '{')) {
+ int previousLine = d.getLineOfOffset(c.offset);
+ buf.append(getIndentOfLine(d, previousLine));
+ buf.append(createIndentation(1));
+ } else {
+ int previousLine = d.getLineOfOffset(c.offset);
+ buf.append(getIndentOfLine(d, previousLine));
+ }
+ c.text = buf.toString();
+ } catch (BadLocationException e) {
+ CssUI.log(
+ "Failed to smart indent after new line", e); //$NON-NLS-1$
+ }
+ }
+
+}