~ubuntu-branches/debian/sid/eclipse-cdt/sid

« back to all changes in this revision

Viewing changes to core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ToggleCommentAction.java

  • Committer: Package Import Robot
  • Author(s): Jakub Adam
  • Date: 2011-10-06 21:15:04 UTC
  • mfrom: (1.1.4)
  • Revision ID: package-import@ubuntu.com-20111006211504-8dutmljjih0zikfv
Tags: 8.0.1-1
* New upstream release.
* Split the JNI packages into a separate architecture dependent
  package and made eclipse-cdt architecture independent.
* Install JNI libraries into multiarch aware location
* Bumped Standards-Version to 3.9.2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*******************************************************************************
 
2
 * Copyright (c) 2006, 2010 IBM Corporation and others.
 
3
 * All rights reserved. This program and the accompanying materials
 
4
 * are made available under the terms of the Eclipse Public License v1.0
 
5
 * which accompanies this distribution, and is available at
 
6
 * http://www.eclipse.org/legal/epl-v10.html
 
7
 *
 
8
 * Contributors:
 
9
 *     IBM Corporation - initial API and implementation
 
10
 *******************************************************************************/
 
11
 
 
12
package org.eclipse.cdt.internal.ui.editor;
 
13
 
 
14
import java.util.HashMap;
 
15
import java.util.Map;
 
16
import java.util.ResourceBundle;
 
17
 
 
18
import org.eclipse.jface.dialogs.MessageDialog;
 
19
import org.eclipse.jface.text.BadLocationException;
 
20
import org.eclipse.jface.text.IDocument;
 
21
import org.eclipse.jface.text.IRegion;
 
22
import org.eclipse.jface.text.ITextOperationTarget;
 
23
import org.eclipse.jface.text.ITextSelection;
 
24
import org.eclipse.jface.text.ITypedRegion;
 
25
import org.eclipse.jface.text.Region;
 
26
import org.eclipse.jface.text.TextUtilities;
 
27
import org.eclipse.jface.text.TextViewer;
 
28
import org.eclipse.jface.text.source.ISourceViewer;
 
29
import org.eclipse.jface.text.source.SourceViewerConfiguration;
 
30
import org.eclipse.jface.viewers.ISelection;
 
31
import org.eclipse.swt.custom.BusyIndicator;
 
32
import org.eclipse.swt.widgets.Display;
 
33
import org.eclipse.swt.widgets.Shell;
 
34
import org.eclipse.ui.texteditor.ITextEditor;
 
35
import org.eclipse.ui.texteditor.ResourceAction;
 
36
import org.eclipse.ui.texteditor.TextEditorAction;
 
37
 
 
38
import org.eclipse.cdt.ui.CUIPlugin;
 
39
 
 
40
/**
 
41
 * An action which toggles comment prefixes on the selected lines.
 
42
 *
 
43
 * @since 4.0.0
 
44
 */
 
45
public final class ToggleCommentAction extends TextEditorAction {
 
46
        /** The text operation target */
 
47
        private ITextOperationTarget fOperationTarget;
 
48
        /** The document partitioning */
 
49
        private String fDocumentPartitioning;
 
50
        /** The comment prefixes */
 
51
        private Map<String, String[]> fPrefixesMap;
 
52
 
 
53
        /**
 
54
         * Creates and initializes the action for the given text editor. The action
 
55
         * configures its visual representation from the given resource bundle.
 
56
         *
 
57
         * @param bundle the resource bundle
 
58
         * @param prefix a prefix to be prepended to the various resource keys
 
59
         *   (described in <code>ResourceAction</code> constructor), or
 
60
         *   <code>null</code> if none
 
61
         * @param editor the text editor
 
62
         * @see ResourceAction#ResourceAction(ResourceBundle, String, int)
 
63
         */
 
64
        public ToggleCommentAction(ResourceBundle bundle, String prefix, ITextEditor editor) {
 
65
                super(bundle, prefix, editor);
 
66
        }
 
67
 
 
68
        /**
 
69
         * Implementation of the <code>IAction</code> prototype. Checks if the selected
 
70
         * lines are all commented or not and uncomments/comments them respectively.
 
71
         */
 
72
        @Override
 
73
        public void run() {
 
74
                if (fOperationTarget == null || fDocumentPartitioning == null || fPrefixesMap == null)
 
75
                        return;
 
76
 
 
77
                ITextEditor editor= getTextEditor();
 
78
                if (editor == null)
 
79
                        return;
 
80
 
 
81
                if (!validateEditorInputState())
 
82
                        return;
 
83
 
 
84
                final int operationCode;
 
85
                if (isSelectionCommented(editor.getSelectionProvider().getSelection()))
 
86
                        operationCode= ITextOperationTarget.STRIP_PREFIX;
 
87
                else
 
88
                        operationCode= ITextOperationTarget.PREFIX;
 
89
 
 
90
                Shell shell= editor.getSite().getShell();
 
91
                if (!fOperationTarget.canDoOperation(operationCode)) {
 
92
                        if (shell != null) {
 
93
                                MessageDialog.openError(shell, CEditorMessages.ToggleComment_error_title,
 
94
                                                CEditorMessages.ToggleComment_error_message);
 
95
                        }
 
96
                        return;
 
97
                }
 
98
 
 
99
                Display display= null;
 
100
                if (shell != null && !shell.isDisposed())
 
101
                        display= shell.getDisplay();
 
102
 
 
103
                BusyIndicator.showWhile(display, new Runnable() {
 
104
                        public void run() {
 
105
                                fOperationTarget.doOperation(operationCode);
 
106
                        }
 
107
                });
 
108
        }
 
109
 
 
110
        /**
 
111
         * Is the given selection single-line commented?
 
112
         *
 
113
         * @param selection Selection to check
 
114
         * @return <code>true</code> iff all selected lines are commented
 
115
         */
 
116
        private boolean isSelectionCommented(ISelection selection) {
 
117
                if (!(selection instanceof ITextSelection))
 
118
                        return false;
 
119
 
 
120
                ITextSelection textSelection= (ITextSelection) selection;
 
121
                if (textSelection.getStartLine() < 0 || textSelection.getEndLine() < 0)
 
122
                        return false;
 
123
 
 
124
                IDocument document= getTextEditor().getDocumentProvider().getDocument(getTextEditor().getEditorInput());
 
125
 
 
126
                try {
 
127
                        IRegion block= getTextBlockFromSelection(textSelection, document);
 
128
                        ITypedRegion[] regions= TextUtilities.computePartitioning(document, fDocumentPartitioning,
 
129
                                        block.getOffset(), block.getLength(), false);
 
130
 
 
131
                        int[] lines= new int[regions.length * 2]; // [startline, endline, startline, endline, ...]
 
132
 
 
133
                        // For each partition in the text selection, figure out the startline and endline.
 
134
                        // Count the number of lines that are selected.
 
135
                        for (int i = 0, j = 0; i < regions.length; i++, j+= 2) {
 
136
                                // Start line of region
 
137
                                lines[j]= getFirstCompleteLineOfRegion(regions[i], document);
 
138
                                // End line of region
 
139
                                int length= regions[i].getLength();
 
140
                                int offset= regions[i].getOffset() + length;
 
141
                                if (length > 0)
 
142
                                        offset--;
 
143
 
 
144
                                // If there is no startline for this region (startline = -1),
 
145
                                // then there is no endline,
 
146
                                // otherwise, get the line number of the endline and store it in the array.
 
147
                                lines[j + 1]= (lines[j] == -1 ? -1 : document.getLineOfOffset(offset));
 
148
 
 
149
                                // We could count the number of lines that are selected in this region
 
150
                                // lineCount += lines[j + 1] - lines[j] + 1;
 
151
 
 
152
                                assert i < regions.length;
 
153
                                assert j < regions.length * 2;
 
154
                        }
 
155
 
 
156
                        // Perform the check
 
157
                        boolean hasComment= false;
 
158
                        for (int i = 0, j = 0; i < regions.length; i++, j += 2) {
 
159
                                String[] prefixes= fPrefixesMap.get(regions[i].getType());
 
160
                                if (prefixes != null && prefixes.length > 0 && lines[j] >= 0 && lines[j + 1] >= 0) {
 
161
                                        if (isBlockCommented(lines[j], lines[j + 1], prefixes, document)) {
 
162
                                                hasComment= true;
 
163
                                        } else if (!isBlockEmpty(lines[j], lines[j + 1], document)) {
 
164
                                                return false;
 
165
                                        }
 
166
                                }
 
167
                        }
 
168
                        return hasComment;
 
169
                } catch (BadLocationException e) {
 
170
                        CUIPlugin.log(e);  // Should not happen
 
171
                }
 
172
 
 
173
                return false;
 
174
        }
 
175
 
 
176
        /**
 
177
         * Creates a region describing the text block (something that starts at
 
178
         * the beginning of a line) completely containing the current selection.
 
179
         *
 
180
         * Note, the implementation has to match {@link TextViewer}.getTextBlockFromSelection().
 
181
         * 
 
182
         * @param selection The selection to use
 
183
         * @param document The document
 
184
         * @return the region describing the text block comprising the given selection
 
185
         * @throws BadLocationException
 
186
         */
 
187
        private IRegion getTextBlockFromSelection(ITextSelection selection, IDocument document) throws BadLocationException {
 
188
                int start= document.getLineOffset(selection.getStartLine());
 
189
                int end;
 
190
                int endLine= selection.getEndLine();
 
191
                if (document.getNumberOfLines() > endLine+1) {
 
192
                        end= document.getLineOffset(endLine+1);
 
193
                } else {
 
194
                        end= document.getLength();
 
195
                }
 
196
                return new Region(start, end - start);
 
197
        }
 
198
 
 
199
        /**
 
200
         * Returns the index of the first line whose start offset is in the given text range.
 
201
         *
 
202
         * @param region the text range in characters where to find the line
 
203
         * @param document The document
 
204
         * @return the first line whose start index is in the given range, -1 if there is no such line
 
205
         */
 
206
        private int getFirstCompleteLineOfRegion(IRegion region, IDocument document) {
 
207
                try {
 
208
                        int startLine= document.getLineOfOffset(region.getOffset());
 
209
 
 
210
                        int offset= document.getLineOffset(startLine);
 
211
                        if (offset >= region.getOffset())
 
212
                                return startLine;
 
213
 
 
214
                        offset= document.getLineOffset(startLine + 1);
 
215
                        return (offset > region.getOffset() + region.getLength() ? -1 : startLine + 1);
 
216
                } catch (BadLocationException e) {
 
217
                        CUIPlugin.log(e);  // Should not happen
 
218
                }
 
219
 
 
220
                return -1;
 
221
        }
 
222
 
 
223
        /**
 
224
         * Determines whether each line is prefixed by one of the prefixes.
 
225
         *
 
226
         * @param startLine Start line in document
 
227
         * @param endLine End line in document
 
228
         * @param prefixes Possible comment prefixes
 
229
         * @param document The document
 
230
         * @return <code>true</code> iff each line from <code>startLine</code>
 
231
         *             to and including <code>endLine</code> is prepended by one
 
232
         *             of the <code>prefixes</code>, ignoring whitespace at the
 
233
         *             begin of line
 
234
         */
 
235
        private boolean isBlockCommented(int startLine, int endLine, String[] prefixes, IDocument document) {
 
236
                try {
 
237
                        // Check for occurrences of prefixes in the given lines
 
238
                        boolean hasComment = false;
 
239
                        for (int i= startLine; i <= endLine; i++) {
 
240
                                IRegion line= document.getLineInformation(i);
 
241
                                String text= document.get(line.getOffset(), line.getLength());
 
242
 
 
243
                                boolean isEmptyLine = text.trim().length() == 0;
 
244
                                if(isEmptyLine) {
 
245
                                        continue;
 
246
                                }
 
247
                                
 
248
                                int[] found= TextUtilities.indexOf(prefixes, text, 0);
 
249
 
 
250
                                if (found[0] == -1) {
 
251
                                        // Found a line which is not commented
 
252
                                        return false;
 
253
                                }
 
254
                                String s= document.get(line.getOffset(), found[0]);
 
255
                                s= s.trim();
 
256
                                if (s.length() != 0) {
 
257
                                        // Found a line which is not commented
 
258
                                        return false;
 
259
                                }
 
260
                                hasComment = true;
 
261
                        }
 
262
                        return hasComment;
 
263
                } catch (BadLocationException e) {
 
264
                        CUIPlugin.log(e);  // Should not happen
 
265
                }
 
266
 
 
267
                return false;
 
268
        }
 
269
 
 
270
        /**
 
271
         * Determines whether each line is empty
 
272
         *
 
273
         * @param startLine Start line in document
 
274
         * @param endLine End line in document
 
275
         * @param document The document
 
276
         * @return <code>true</code> if each line from <code>startLine</code>
 
277
         *             to and including <code>endLine</code> is empty
 
278
         */
 
279
        private boolean isBlockEmpty(int startLine, int endLine, IDocument document) {
 
280
                try {
 
281
                        for (int i= startLine; i <= endLine; i++) {
 
282
                                IRegion line= document.getLineInformation(i);
 
283
                                String text= document.get(line.getOffset(), line.getLength());
 
284
        
 
285
                                boolean isEmptyLine = text.trim().length() == 0;
 
286
                                if(!isEmptyLine) {
 
287
                                        return false;
 
288
                                }
 
289
                        }
 
290
                        return true;
 
291
                } catch (BadLocationException e) {
 
292
                        CUIPlugin.log(e);  // Should not happen
 
293
                }
 
294
        
 
295
                return false;
 
296
        }
 
297
 
 
298
        /**
 
299
         * Implementation of the <code>IUpdate</code> prototype method discovers
 
300
         * the operation through the current editor's
 
301
         * <code>ITextOperationTarget</code> adapter, and sets the enabled state
 
302
         * accordingly.
 
303
         */
 
304
        @Override
 
305
        public void update() {
 
306
                super.update();
 
307
 
 
308
                if (!canModifyEditor()) {
 
309
                        setEnabled(false);
 
310
                        return;
 
311
                }
 
312
 
 
313
                ITextEditor editor= getTextEditor();
 
314
                if (fOperationTarget == null && editor != null)
 
315
                        fOperationTarget= (ITextOperationTarget) editor.getAdapter(ITextOperationTarget.class);
 
316
 
 
317
                boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(ITextOperationTarget.PREFIX) &&
 
318
                                fOperationTarget.canDoOperation(ITextOperationTarget.STRIP_PREFIX));
 
319
                setEnabled(isEnabled);
 
320
        }
 
321
 
 
322
        /*
 
323
         * @see TextEditorAction#setEditor(ITextEditor)
 
324
         */
 
325
        @Override
 
326
        public void setEditor(ITextEditor editor) {
 
327
                super.setEditor(editor);
 
328
                fOperationTarget= null;
 
329
        }
 
330
 
 
331
        /**
 
332
         * For the different content types, get its default comment prefix and store the prefixes.
 
333
         * @param sourceViewer
 
334
         * @param configuration
 
335
         */
 
336
        public void configure(ISourceViewer sourceViewer, SourceViewerConfiguration configuration) {
 
337
                fPrefixesMap= null;
 
338
 
 
339
                String[] types= configuration.getConfiguredContentTypes(sourceViewer);
 
340
                Map<String, String[]> prefixesMap= new HashMap<String, String[]>(types.length);
 
341
                for (String type : types) {
 
342
                        String[] prefixes= configuration.getDefaultPrefixes(sourceViewer, type);
 
343
                        if (prefixes != null && prefixes.length > 0) {
 
344
                                int emptyPrefixes= 0;
 
345
                                for (String prefixe : prefixes) {
 
346
                                        if (prefixe.length() == 0)
 
347
                                                emptyPrefixes++;
 
348
                                }
 
349
 
 
350
                                if (emptyPrefixes > 0) {
 
351
                                        String[] nonemptyPrefixes= new String[prefixes.length - emptyPrefixes];
 
352
                                        for (int j= 0, k= 0; j < prefixes.length; j++) {
 
353
                                                String prefix= prefixes[j];
 
354
                                                if (prefix.length() != 0) {
 
355
                                                        nonemptyPrefixes[k]= prefix;
 
356
                                                        k++;
 
357
                                                }
 
358
                                        }
 
359
                                        prefixes= nonemptyPrefixes;
 
360
                                }
 
361
 
 
362
                                prefixesMap.put(type, prefixes);
 
363
                        }
 
364
                }
 
365
                fDocumentPartitioning= configuration.getConfiguredDocumentPartitioning(sourceViewer);
 
366
                fPrefixesMap= prefixesMap;
 
367
        }
 
368
}