intial source from ttp://www.sf.net/projects/wdte
[phpeclipse.git] / archive / net.sourceforge.phpeclipse.css.ui / src / net / sourceforge / phpeclipse / css / ui / internal / text / CssAutoEditStrategy.java
1 /*
2  * Copyright (c) 2003-2004 Christopher Lenz and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     Christopher Lenz - initial API and implementation
10  * 
11  * $Id: CssAutoEditStrategy.java,v 1.1 2004-09-02 18:11:48 jsurfer Exp $
12  */
13
14 package net.sourceforge.phpeclipse.css.ui.internal.text;
15
16 import net.sourceforge.phpeclipse.css.core.internal.text.CssTextUtils;
17 import net.sourceforge.phpeclipse.css.ui.CssUI;
18 import net.sourceforge.phpeclipse.css.ui.internal.CssUIPreferences;
19
20 import org.eclipse.jface.preference.IPreferenceStore;
21 import org.eclipse.jface.text.BadLocationException;
22 import org.eclipse.jface.text.DefaultAutoIndentStrategy;
23 import org.eclipse.jface.text.DefaultLineTracker;
24 import org.eclipse.jface.text.DocumentCommand;
25 import org.eclipse.jface.text.IDocument;
26 import org.eclipse.jface.text.ILineTracker;
27 import org.eclipse.jface.text.IRegion;
28 import org.eclipse.ui.IEditorPart;
29 import org.eclipse.ui.IWorkbenchPage;
30 import org.eclipse.ui.IWorkbenchWindow;
31 import org.eclipse.ui.PlatformUI;
32 import org.eclipse.ui.texteditor.ITextEditorExtension3;
33 import org.eclipse.ui.texteditor.ITextEditorExtension3.InsertMode;
34
35 /**
36  * Implements various auto-editing aspects for editing CSS:
37  * <ul>
38  *  <li>
39  *   Smart indenting after opening or closing a block
40  *  </li>
41  *  <li>
42  *   Smart insertion of closing braces
43  *  </li>
44  * </ul>
45  * All these only operate when 'Smart Insert' mode is enabled, and the closing
46  * of braces, brackets, parenthesis and strings can be disabled in the editor
47  * preferences.
48  */
49 public class CssAutoEditStrategy extends DefaultAutoIndentStrategy {
50
51         // Constants ---------------------------------------------------------------
52
53         /**
54          * Alias for the preference constant <code>EDITOR_SPACES_FOR_TABS</code>.
55          */
56         public static final String SPACES_FOR_TABS =
57                 CssUIPreferences.EDITOR_SPACES_FOR_TABS;
58
59         /**
60          * Alias for the preference constant <code>EDITOR_TAB_WIDTH</code>.
61          */
62         public static final String TAB_WIDTH =
63                 CssUIPreferences.EDITOR_TAB_WIDTH;
64
65         // Instance Variables ------------------------------------------------------
66
67         /**
68          * The line tracker.
69          */
70         private ILineTracker lineTracker;
71
72         /**
73          * The preference store.
74          */
75         private IPreferenceStore store;
76
77         // Constructors ------------------------------------------------------------
78
79         /**
80          * Default constructor.
81          */
82         public CssAutoEditStrategy() {
83                 lineTracker = new DefaultLineTracker();
84                 store = CssUI.getDefault().getPreferenceStore();
85         }
86
87         // DefaultAutoIndentStrategy Implementation --------------------------------
88
89         /**
90          * @see org.eclipse.jface.text.IAutoEditStrategy#customizeDocumentCommand(IDocument, DocumentCommand)
91          */
92         public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
93                 if (isSmartInsertMode()) {
94                         if (isNewLine(d, c)) {
95                                 smartIndentAfterNewLine(d, c);
96                         } else if (c.text != null) {
97                                 if (c.text.length() == 1) {
98                                         switch (c.text.charAt(0)) {
99                                                 case '}': {
100                                                         smartInsertClosingBrace(d, c);
101                                                         break;
102                                                 }
103                                                 default: {
104                                                         // do nothing
105                                                 }
106                                         }
107                                 }
108                                 if (store.getBoolean(SPACES_FOR_TABS)) {
109                                         convertTabs(d, c);
110                                 }
111                         }
112                 }
113         }
114
115         // Private Methods ---------------------------------------------------------
116
117         private void convertTabs(IDocument d, DocumentCommand c) {
118                 int index = c.text.indexOf('\t');
119                 if (index > -1) {
120                         StringBuffer buffer = new StringBuffer();
121                         lineTracker.set(c.text);
122                         int lines = lineTracker.getNumberOfLines();
123                         try {
124                                 for (int i = 0; i < lines; i++) {
125                                         int offset = lineTracker.getLineOffset(i);
126                                         int endOffset = offset + lineTracker.getLineLength(i);
127                                         String line = c.text.substring(offset, endOffset);
128                                         int position = 0;
129                                         if (i == 0) {
130                                                 IRegion firstLine =
131                                                         d.getLineInformationOfOffset(c.offset);
132                                                 position = c.offset - firstLine.getOffset();   
133                                         }
134                                         int length = line.length();
135                                         for (int j = 0; j < length; j++) {
136                                                 char ch = line.charAt(j);
137                                                 if (ch == '\t') {
138                                                         int tabWidth = getTabWidth();
139                                                         int remainder = position % tabWidth;
140                                                         remainder = tabWidth - remainder;
141                                                         for (int k = 0; k < remainder; k++) {
142                                                                 buffer.append(' ');
143                                                         }
144                                                         position += remainder;
145                                                 } else {
146                                                         buffer.append(ch);
147                                                         position++;
148                                                 }
149                                         }
150                                 }
151                                 c.text = buffer.toString();
152                         } catch (BadLocationException e) {
153                                 // ignore
154                         }
155                 }
156         }
157
158         private String getIndentOfLine(IDocument d, int line)
159                 throws BadLocationException {
160                 if (line > -1) {
161                         int start = d.getLineOffset(line);
162                         int end = start + d.getLineLength(line) - 1;
163                         int whiteEnd = findEndOfWhiteSpace(d, start, end);
164                         return d.get(start, whiteEnd - start);
165                 } else {
166                         return ""; //$NON-NLS-1$
167                 }
168         }
169
170         private int getTabWidth() {
171                 return store.getInt(TAB_WIDTH);
172         }
173
174         private boolean isNewLine(IDocument d, DocumentCommand c) {
175                 if ((c.length == 0) && (c.text != null)) {
176                         String[] delimiters = d.getLegalLineDelimiters();
177                         for (int i = 0; i < delimiters.length; i++) {
178                                 if (c.text.equals(delimiters[i])) {
179                                         return true;
180                                 }
181                         }
182                 }
183                 return false;
184         }
185
186         private boolean isSmartInsertMode() {
187                 IWorkbenchWindow window =
188                         PlatformUI.getWorkbench().getActiveWorkbenchWindow();
189                 if (window != null) {
190                         IWorkbenchPage page = window.getActivePage();
191                         if (page != null)  {
192                                 IEditorPart part = page.getActiveEditor(); 
193                                 if (part instanceof ITextEditorExtension3) {
194                                         InsertMode insertMode =
195                                                 ((ITextEditorExtension3) part).getInsertMode();
196                                         return (insertMode == ITextEditorExtension3.SMART_INSERT);
197                                 }
198                         }
199                 }
200                 return false;
201         }
202
203         private String createIndentation(int level) {
204                 StringBuffer buf = new StringBuffer();
205                 if (store.getBoolean(SPACES_FOR_TABS)) {
206                         int tabWidth = getTabWidth();
207                         for (int i = 0; i < level * tabWidth; i++) {
208                                 buf.append(' ');
209                         }
210                 } else {
211                         for (int i = 0; i < level; i++) {
212                                 buf.append('\t');
213                         }
214                 }
215                 return buf.toString();
216         }
217
218         private void smartInsertClosingBrace(IDocument d, DocumentCommand c) {
219                 int docLength = d.getLength();
220                 if ((c.offset == -1) || (docLength == 0)) {
221                         return;
222                 }
223                 try {
224                         int lineOffset = d.getLineInformationOfOffset(c.offset).getOffset();
225                         if (c.offset == findEndOfWhiteSpace(d, lineOffset, c.offset)) {
226                                 int openingBraceOffset =
227                                         CssTextUtils.findMatchingOpeningPeer(d, c.offset - 1, '}');
228                                 if (openingBraceOffset >= 0) {
229                                         int openingBraceLine =
230                                                 d.getLineOfOffset(openingBraceOffset);
231                                         StringBuffer buf =
232                                                 new StringBuffer(getIndentOfLine(d, openingBraceLine));
233                                         buf.append(c.text);
234                                         c.length += c.offset - lineOffset;
235                                         c.offset = lineOffset;
236                                         c.text = buf.toString();
237                                 }
238                         }
239                 } catch (BadLocationException e) {
240                         CssUI.log(
241                                 "Failed to smart indent after closing brace", e); //$NON-NLS-1$
242                 }
243         }
244
245         private void smartIndentAfterNewLine(IDocument d, DocumentCommand c) {
246                 try {
247                         int docLength = d.getLength();
248                         if ((c.offset == -1) || (docLength == 0)) {
249                                 return;
250                         }
251                         StringBuffer buf = new StringBuffer(c.text);
252                         if ((c.offset < docLength) && (d.getChar(c.offset - 1) == '}')) {
253                                 // indent with the same amount used before the block was opened 
254                                 int openingBraceOffset =
255                                         CssTextUtils.findMatchingOpeningPeer(d, c.offset - 2, '}');
256                                 if (openingBraceOffset >= 0) {
257                                         int openingBraceLine =
258                                                 d.getLineOfOffset(openingBraceOffset);
259                                         buf.append(getIndentOfLine(d, openingBraceLine));
260                                 }
261                         } else if ((c.offset < docLength)
262                                         && (d.getChar(c.offset - 1) == '{')) {
263                                 int previousLine = d.getLineOfOffset(c.offset);
264                                 buf.append(getIndentOfLine(d, previousLine));
265                                 buf.append(createIndentation(1));
266                         } else {
267                                 int previousLine = d.getLineOfOffset(c.offset);
268                                 buf.append(getIndentOfLine(d, previousLine));
269                         }
270                         c.text = buf.toString();
271                 } catch (BadLocationException e) {
272                         CssUI.log(
273                                 "Failed to smart indent after new line", e); //$NON-NLS-1$
274                 }
275         }
276
277 }