New upstream version 4.12
Emmanuel Bourg
4 years ago
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %pluginName |
3 | 3 | Bundle-SymbolicName: org.eclipse.core.filebuffers; singleton:=true |
4 | Bundle-Version: 3.6.500.qualifier | |
4 | Bundle-Version: 3.6.600.qualifier | |
5 | 5 | Bundle-Activator: org.eclipse.core.internal.filebuffers.FileBuffersPlugin |
6 | 6 | Bundle-ActivationPolicy: lazy |
7 | 7 | Bundle-Vendor: %providerName |
13 | 13 | <parent> |
14 | 14 | <artifactId>eclipse.platform.text</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | </parent> |
18 | 18 | <groupId>org.eclipse.core</groupId> |
19 | 19 | <artifactId>org.eclipse.core.filebuffers</artifactId> |
20 | <version>3.6.500-SNAPSHOT</version> | |
20 | <version>3.6.600-SNAPSHOT</version> | |
21 | 21 | <packaging>eclipse-plugin</packaging> |
22 | 22 | </project> |
+1
-1
95 | 95 | ITextFileBuffer getFileStoreTextFileBuffer(IFileStore fileStore); |
96 | 96 | |
97 | 97 | /** |
98 | * Returns the text file buffer managed for the given document | |
98 | * Returns the text file buffer managed for the given document | |
99 | 99 | * or <code>null</code> if there is no such text file buffer. |
100 | 100 | * <p> |
101 | 101 | * <strong>Note:</strong> This method goes through the list |
31 | 31 | * connected using a file store will not be found. |
32 | 32 | * </p> |
33 | 33 | * |
34 | * @see IFileBufferManager#connectFileStore(org.eclipse.core.filesystem.IFileStore, org.eclipse.core.runtime.IProgressMonitor) | |
34 | * @see IFileBufferManager#connectFileStore(org.eclipse.core.filesystem.IFileStore, org.eclipse.core.runtime.IProgressMonitor) | |
35 | 35 | */ |
36 | 36 | public static final LocationKind LOCATION= new LocationKind("location"); //$NON-NLS-1$ |
37 | 37 |
+2
-2
112 | 112 | /** Tells whether the file on disk has a BOM. */ |
113 | 113 | private boolean fHasBOM; |
114 | 114 | /** The annotation model of this file buffer */ |
115 | private IAnnotationModel fAnnotationModel; | |
115 | private IAnnotationModel fAnnotationModel; | |
116 | 116 | /** |
117 | 117 | * Lock for lazy creation of annotation model. |
118 | 118 | * @since 3.2 |
257 | 257 | if (fAnnotationModel instanceof IPersistableAnnotationModel) { |
258 | 258 | IPersistableAnnotationModel persistableModel= (IPersistableAnnotationModel) fAnnotationModel; |
259 | 259 | try { |
260 | persistableModel.revert(fDocument); | |
260 | persistableModel.revert(fDocument); | |
261 | 261 | } catch (CoreException x) { |
262 | 262 | fStatus= x.getStatus(); |
263 | 263 | } |
13 | 13 | <parent> |
14 | 14 | <artifactId>tests-pom</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | <relativePath>../tests-pom/</relativePath> |
18 | 18 | </parent> |
19 | 19 | <groupId>org.eclipse.core</groupId> |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %pluginName |
3 | 3 | Bundle-SymbolicName: org.eclipse.jface.text |
4 | Bundle-Version: 3.15.100.qualifier | |
4 | Bundle-Version: 3.15.200.qualifier | |
5 | 5 | Bundle-Vendor: %providerName |
6 | 6 | Bundle-Localization: plugin |
7 | 7 | Export-Package: |
13 | 13 | <parent> |
14 | 14 | <artifactId>eclipse.platform.text</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | </parent> |
18 | 18 | <groupId>org.eclipse.jface</groupId> |
19 | 19 | <artifactId>org.eclipse.jface.text</artifactId> |
20 | <version>3.15.100-SNAPSHOT</version> | |
20 | <version>3.15.200-SNAPSHOT</version> | |
21 | 21 | <packaging>eclipse-plugin</packaging> |
22 | 22 | </project> |
+34
-34
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.internal.text.codemining; | |
14 | ||
15 | import java.util.concurrent.CancellationException; | |
16 | ||
17 | import org.eclipse.core.runtime.IProgressMonitor; | |
18 | import org.eclipse.core.runtime.NullProgressMonitor; | |
19 | ||
20 | /** | |
21 | * {@link IProgressMonitor} which throws a {@link CancellationException} when | |
22 | * {@link IProgressMonitor#isCanceled()} returns true. | |
23 | * | |
24 | * @since 3.13 | |
25 | */ | |
26 | class CancellationExceptionMonitor extends NullProgressMonitor { | |
27 | ||
28 | @Override | |
29 | public boolean isCanceled() { | |
30 | boolean canceled= super.isCanceled(); | |
31 | if (canceled) { | |
32 | throw new CancellationException(); | |
33 | } | |
34 | return canceled; | |
35 | } | |
36 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.internal.text.codemining; | |
14 | ||
15 | import java.util.concurrent.CancellationException; | |
16 | ||
17 | import org.eclipse.core.runtime.IProgressMonitor; | |
18 | import org.eclipse.core.runtime.NullProgressMonitor; | |
19 | ||
20 | /** | |
21 | * {@link IProgressMonitor} which throws a {@link CancellationException} when | |
22 | * {@link IProgressMonitor#isCanceled()} returns true. | |
23 | * | |
24 | * @since 3.13 | |
25 | */ | |
26 | class CancellationExceptionMonitor extends NullProgressMonitor { | |
27 | ||
28 | @Override | |
29 | public boolean isCanceled() { | |
30 | boolean canceled= super.isCanceled(); | |
31 | if (canceled) { | |
32 | throw new CancellationException(); | |
33 | } | |
34 | return canceled; | |
35 | } | |
36 | } |
+325
-325
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.internal.text.codemining; | |
14 | ||
15 | import java.util.Arrays; | |
16 | import java.util.Collections; | |
17 | import java.util.HashSet; | |
18 | import java.util.LinkedHashMap; | |
19 | import java.util.List; | |
20 | import java.util.Map; | |
21 | import java.util.Set; | |
22 | import java.util.concurrent.CancellationException; | |
23 | import java.util.concurrent.CompletableFuture; | |
24 | import java.util.function.Function; | |
25 | import java.util.stream.Collectors; | |
26 | ||
27 | import org.osgi.framework.Bundle; | |
28 | ||
29 | import org.eclipse.swt.graphics.Rectangle; | |
30 | ||
31 | import org.eclipse.core.runtime.Assert; | |
32 | import org.eclipse.core.runtime.ILog; | |
33 | import org.eclipse.core.runtime.IProgressMonitor; | |
34 | import org.eclipse.core.runtime.IStatus; | |
35 | import org.eclipse.core.runtime.Platform; | |
36 | import org.eclipse.core.runtime.Status; | |
37 | ||
38 | import org.eclipse.jface.text.IDocument; | |
39 | import org.eclipse.jface.text.ITextViewer; | |
40 | import org.eclipse.jface.text.Position; | |
41 | import org.eclipse.jface.text.codemining.ICodeMining; | |
42 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
43 | import org.eclipse.jface.text.codemining.LineHeaderCodeMining; | |
44 | import org.eclipse.jface.text.source.ISourceViewer; | |
45 | import org.eclipse.jface.text.source.inlined.AbstractInlinedAnnotation; | |
46 | import org.eclipse.jface.text.source.inlined.InlinedAnnotationSupport; | |
47 | ||
48 | /** | |
49 | * Code Mining manager implementation. | |
50 | * | |
51 | * @since 3.13 | |
52 | */ | |
53 | public class CodeMiningManager implements Runnable { | |
54 | ||
55 | /** | |
56 | * The source viewer | |
57 | */ | |
58 | private final ISourceViewer fViewer; | |
59 | ||
60 | /** | |
61 | * The inlined annotation support used to draw CodeMining in the line spacing. | |
62 | */ | |
63 | private final InlinedAnnotationSupport fInlinedAnnotationSupport; | |
64 | ||
65 | /** | |
66 | * The list of codemining providers. | |
67 | */ | |
68 | private List<ICodeMiningProvider> fCodeMiningProviders; | |
69 | ||
70 | /** | |
71 | * The current progress monitor. | |
72 | */ | |
73 | private IProgressMonitor fMonitor; | |
74 | ||
75 | /** | |
76 | * Constructor of codemining manager with the given arguments. | |
77 | * | |
78 | * @param viewer the source viewer | |
79 | * @param inlinedAnnotationSupport the inlined annotation support used to draw code minings | |
80 | * @param codeMiningProviders the array of codemining providers, must not be empty | |
81 | */ | |
82 | public CodeMiningManager(ISourceViewer viewer, InlinedAnnotationSupport inlinedAnnotationSupport, | |
83 | ICodeMiningProvider[] codeMiningProviders) { | |
84 | Assert.isNotNull(viewer); | |
85 | Assert.isNotNull(inlinedAnnotationSupport); | |
86 | Assert.isNotNull(codeMiningProviders); | |
87 | fViewer= viewer; | |
88 | fInlinedAnnotationSupport= inlinedAnnotationSupport; | |
89 | setCodeMiningProviders(codeMiningProviders); | |
90 | } | |
91 | ||
92 | /** | |
93 | * Set the codemining providers. | |
94 | * | |
95 | * @param codeMiningProviders the codemining providers. | |
96 | */ | |
97 | public void setCodeMiningProviders(ICodeMiningProvider[] codeMiningProviders) { | |
98 | cancel(); | |
99 | if (fCodeMiningProviders != null) { | |
100 | fCodeMiningProviders.stream().forEach(ICodeMiningProvider::dispose); | |
101 | } | |
102 | fCodeMiningProviders= Arrays.asList(codeMiningProviders); | |
103 | } | |
104 | ||
105 | /** | |
106 | * Uninstalls this codemining manager. | |
107 | */ | |
108 | public void uninstall() { | |
109 | cancel(); | |
110 | if (fInlinedAnnotationSupport != null) { | |
111 | fInlinedAnnotationSupport.updateAnnotations(Collections.emptySet()); | |
112 | } | |
113 | } | |
114 | ||
115 | /** | |
116 | * Collect, resolve and render the code minings of the viewer. | |
117 | */ | |
118 | @Override | |
119 | public void run() { | |
120 | if (fViewer == null || fInlinedAnnotationSupport == null || fCodeMiningProviders == null | |
121 | || fCodeMiningProviders.size() == 0 || fViewer.getAnnotationModel() == null) { | |
122 | return; | |
123 | } | |
124 | // Cancel the last progress monitor to cancel last resolve and render of code | |
125 | // minings | |
126 | cancel(); | |
127 | // Update the code minings | |
128 | updateCodeMinings(); | |
129 | } | |
130 | ||
131 | /** | |
132 | * Update the code minings. | |
133 | */ | |
134 | private void updateCodeMinings() { | |
135 | // Refresh the code minings by using the new progress monitor. | |
136 | fMonitor= new CancellationExceptionMonitor(); | |
137 | IProgressMonitor monitor= fMonitor; | |
138 | // Collect the code minings for the viewer | |
139 | getCodeMinings(fViewer, fCodeMiningProviders, monitor).thenAccept(symbols -> { | |
140 | // check if request was canceled. | |
141 | monitor.isCanceled(); | |
142 | // then group code minings by lines position | |
143 | Map<Position, List<ICodeMining>> groups= groupByLines(symbols, fCodeMiningProviders); | |
144 | // resolve and render code minings | |
145 | renderCodeMinings(groups, fViewer, monitor); | |
146 | }); | |
147 | } | |
148 | ||
149 | /** | |
150 | * Cancel the codemining process. | |
151 | */ | |
152 | private void cancel() { | |
153 | // Cancel the last progress monitor. | |
154 | if (fMonitor != null) { | |
155 | fMonitor.setCanceled(true); | |
156 | } | |
157 | } | |
158 | ||
159 | private static void logCodeMiningProviderException(Throwable e) { | |
160 | if (e != null && (e instanceof CancellationException || (e.getCause() != null && e.getCause() instanceof CancellationException))) { | |
161 | return; | |
162 | } | |
163 | String PLUGIN_ID= "org.eclipse.jface.text"; //$NON-NLS-1$ | |
164 | Bundle plugin= Platform.getBundle(PLUGIN_ID); | |
165 | if (plugin != null) { | |
166 | // In OSGi context, uses Platform Text log | |
167 | ILog log= Platform.getLog(plugin); | |
168 | log.log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, e.getMessage(), e)); | |
169 | } else { | |
170 | // In java main context, print stack trace | |
171 | System.err.println("Error while code mining process: " + e.getMessage()); //$NON-NLS-1$ | |
172 | } | |
173 | } | |
174 | ||
175 | /** | |
176 | * Return the list of {@link CompletableFuture} which provides the list of {@link ICodeMining} | |
177 | * for the given <code>viewer</code> by using the given providers. | |
178 | * | |
179 | * @param viewer the text viewer. | |
180 | * @param providers the CodeMining list providers. | |
181 | * @param monitor the progress monitor. | |
182 | * @return the list of {@link CompletableFuture} which provides the list of {@link ICodeMining} | |
183 | * for the given <code>viewer</code> by using the given providers. | |
184 | */ | |
185 | private static CompletableFuture<List<? extends ICodeMining>> getCodeMinings(ITextViewer viewer, | |
186 | List<ICodeMiningProvider> providers, IProgressMonitor monitor) { | |
187 | List<CompletableFuture<List<? extends ICodeMining>>> com= providers.stream() | |
188 | .map(provider -> provider.provideCodeMinings(viewer, monitor)) | |
189 | .filter(c -> c != null) | |
190 | .map(future -> future.exceptionally(e -> { | |
191 | logCodeMiningProviderException(e); | |
192 | return Collections.emptyList(); | |
193 | })) | |
194 | .collect(Collectors.toList()); | |
195 | return CompletableFuture.allOf(com.toArray(new CompletableFuture[com.size()])).thenApply( | |
196 | v -> com.stream().map(CompletableFuture::join).flatMap(l -> l.stream()).collect(Collectors.toList())); | |
197 | } | |
198 | ||
199 | /** | |
200 | * Returns a sorted Map which groups the given code minings by same position line. | |
201 | * | |
202 | * @param codeMinings list of code minings to group. | |
203 | * @param providers CodeMining providers used to retrieve code minings. | |
204 | * @return a sorted Map which groups the given code minings by same position line. | |
205 | */ | |
206 | private static Map<Position, List<ICodeMining>> groupByLines(List<? extends ICodeMining> codeMinings, | |
207 | List<ICodeMiningProvider> providers) { | |
208 | // sort code minings by lineNumber and provider-rank if | |
209 | Collections.sort(codeMinings, (a, b) -> { | |
210 | if (a.getPosition().offset < b.getPosition().offset) { | |
211 | return -1; | |
212 | } else if (a.getPosition().offset > b.getPosition().offset) { | |
213 | return 1; | |
214 | } else if (providers.indexOf(a.getProvider()) < providers.indexOf(b.getProvider())) { | |
215 | return -1; | |
216 | } else if (providers.indexOf(a.getProvider()) > providers.indexOf(b.getProvider())) { | |
217 | return 1; | |
218 | } else { | |
219 | return 0; | |
220 | } | |
221 | }); | |
222 | return codeMinings.stream().collect(Collectors.groupingBy(ICodeMining::getPosition, LinkedHashMap::new, | |
223 | Collectors.mapping(Function.identity(), Collectors.toList()))); | |
224 | } | |
225 | ||
226 | /** | |
227 | * Render the codemining grouped by line position. | |
228 | * | |
229 | * @param groups code minings grouped by lines position | |
230 | * @param viewer the viewer | |
231 | * @param monitor the progress monitor | |
232 | */ | |
233 | private void renderCodeMinings(Map<Position, List<ICodeMining>> groups, ISourceViewer viewer, | |
234 | IProgressMonitor monitor) { | |
235 | // check if request was canceled. | |
236 | monitor.isCanceled(); | |
237 | IDocument document= viewer != null ? viewer.getDocument() : null; | |
238 | if (document == null) { | |
239 | // this case comes from when editor is closed before codemining rendered is | |
240 | // done. | |
241 | return; | |
242 | } | |
243 | Set<ICodeMiningAnnotation> annotationsToRedraw= new HashSet<>(); | |
244 | Set<AbstractInlinedAnnotation> currentAnnotations= new HashSet<>(); | |
245 | // Loop for grouped code minings | |
246 | groups.entrySet().stream().forEach(g -> { | |
247 | // check if request was canceled. | |
248 | monitor.isCanceled(); | |
249 | ||
250 | Position pos= new Position(g.getKey().offset, g.getKey().length); | |
251 | List<ICodeMining> minings= g.getValue(); | |
252 | boolean inLineHeader= minings.size() > 0 ? (minings.get(0) instanceof LineHeaderCodeMining) : true; | |
253 | // Try to find existing annotation | |
254 | AbstractInlinedAnnotation ann= fInlinedAnnotationSupport.findExistingAnnotation(pos); | |
255 | if (ann == null) { | |
256 | // The annotation doesn't exists, create it. | |
257 | ann= inLineHeader ? new CodeMiningLineHeaderAnnotation(pos, viewer) : new CodeMiningLineContentAnnotation(pos, viewer); | |
258 | } else if (ann instanceof ICodeMiningAnnotation && ((ICodeMiningAnnotation) ann).isInVisibleLines()) { | |
259 | // annotation is in visible lines | |
260 | annotationsToRedraw.add((ICodeMiningAnnotation) ann); | |
261 | } | |
262 | ((ICodeMiningAnnotation) ann).update(minings, monitor); | |
263 | currentAnnotations.add(ann); | |
264 | }); | |
265 | // check if request was canceled. | |
266 | monitor.isCanceled(); | |
267 | fInlinedAnnotationSupport.updateAnnotations(currentAnnotations); | |
268 | // redraw the existing codemining annotations since their content can change | |
269 | annotationsToRedraw.stream().forEach(ann -> ann.redraw()); | |
270 | } | |
271 | ||
272 | /** | |
273 | * Returns <code>true</code> if the given mining has a non empty label and <code>false</code> | |
274 | * otherwise. | |
275 | * | |
276 | * @param mining the mining to check | |
277 | * @return <code>true</code> if the given mining has a non empty label and <code>false</code> | |
278 | * otherwise. | |
279 | */ | |
280 | static boolean isValidMining(ICodeMining mining) { | |
281 | return mining != null && mining.getLabel() != null && !mining.getLabel().isEmpty(); | |
282 | } | |
283 | ||
284 | /** | |
285 | * Returns the valid code mining at the given location by using the bounds of codemining | |
286 | * annotations which stores only the valid code mining. | |
287 | * | |
288 | * @param minings the list of mining of the codemining annotation. | |
289 | * @param bounds the bounds of the valid minings of the codemining annotation. | |
290 | * @param x the x location | |
291 | * @param y the y location | |
292 | * @return the valid code mining at the given location by using the bounds of codemining | |
293 | * annotations which stores only the valid code mining. | |
294 | */ | |
295 | static ICodeMining getValidCodeMiningAtLocation(ICodeMining[] minings, List<Rectangle> bounds, int x, int y) { | |
296 | for (int i= 0; i < bounds.size(); i++) { | |
297 | Rectangle bound= bounds.get(i); | |
298 | if (bound.contains(x, y)) { | |
299 | return getCodeValidMiningAtIndex(minings, i); | |
300 | } | |
301 | } | |
302 | return null; | |
303 | } | |
304 | ||
305 | /** | |
306 | * Returns the valid code mining at the given index. | |
307 | * | |
308 | * @param minings the list of minings | |
309 | * @param index the index | |
310 | * @return the valid code mining at the given index. | |
311 | */ | |
312 | private static ICodeMining getCodeValidMiningAtIndex(ICodeMining[] minings, int index) { | |
313 | int validIndex= 0; | |
314 | for (ICodeMining mining : minings) { | |
315 | if (isValidMining(mining)) { | |
316 | if (validIndex == index) { | |
317 | return mining; | |
318 | } | |
319 | validIndex++; | |
320 | } | |
321 | } | |
322 | return null; | |
323 | } | |
324 | } | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.internal.text.codemining; | |
14 | ||
15 | import java.util.Arrays; | |
16 | import java.util.Collections; | |
17 | import java.util.HashSet; | |
18 | import java.util.LinkedHashMap; | |
19 | import java.util.List; | |
20 | import java.util.Map; | |
21 | import java.util.Set; | |
22 | import java.util.concurrent.CancellationException; | |
23 | import java.util.concurrent.CompletableFuture; | |
24 | import java.util.function.Function; | |
25 | import java.util.stream.Collectors; | |
26 | ||
27 | import org.osgi.framework.Bundle; | |
28 | ||
29 | import org.eclipse.swt.graphics.Rectangle; | |
30 | ||
31 | import org.eclipse.core.runtime.Assert; | |
32 | import org.eclipse.core.runtime.ILog; | |
33 | import org.eclipse.core.runtime.IProgressMonitor; | |
34 | import org.eclipse.core.runtime.IStatus; | |
35 | import org.eclipse.core.runtime.Platform; | |
36 | import org.eclipse.core.runtime.Status; | |
37 | ||
38 | import org.eclipse.jface.text.IDocument; | |
39 | import org.eclipse.jface.text.ITextViewer; | |
40 | import org.eclipse.jface.text.Position; | |
41 | import org.eclipse.jface.text.codemining.ICodeMining; | |
42 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
43 | import org.eclipse.jface.text.codemining.LineHeaderCodeMining; | |
44 | import org.eclipse.jface.text.source.ISourceViewer; | |
45 | import org.eclipse.jface.text.source.inlined.AbstractInlinedAnnotation; | |
46 | import org.eclipse.jface.text.source.inlined.InlinedAnnotationSupport; | |
47 | ||
48 | /** | |
49 | * Code Mining manager implementation. | |
50 | * | |
51 | * @since 3.13 | |
52 | */ | |
53 | public class CodeMiningManager implements Runnable { | |
54 | ||
55 | /** | |
56 | * The source viewer | |
57 | */ | |
58 | private final ISourceViewer fViewer; | |
59 | ||
60 | /** | |
61 | * The inlined annotation support used to draw CodeMining in the line spacing. | |
62 | */ | |
63 | private final InlinedAnnotationSupport fInlinedAnnotationSupport; | |
64 | ||
65 | /** | |
66 | * The list of codemining providers. | |
67 | */ | |
68 | private List<ICodeMiningProvider> fCodeMiningProviders; | |
69 | ||
70 | /** | |
71 | * The current progress monitor. | |
72 | */ | |
73 | private IProgressMonitor fMonitor; | |
74 | ||
75 | /** | |
76 | * Constructor of codemining manager with the given arguments. | |
77 | * | |
78 | * @param viewer the source viewer | |
79 | * @param inlinedAnnotationSupport the inlined annotation support used to draw code minings | |
80 | * @param codeMiningProviders the array of codemining providers, must not be empty | |
81 | */ | |
82 | public CodeMiningManager(ISourceViewer viewer, InlinedAnnotationSupport inlinedAnnotationSupport, | |
83 | ICodeMiningProvider[] codeMiningProviders) { | |
84 | Assert.isNotNull(viewer); | |
85 | Assert.isNotNull(inlinedAnnotationSupport); | |
86 | Assert.isNotNull(codeMiningProviders); | |
87 | fViewer= viewer; | |
88 | fInlinedAnnotationSupport= inlinedAnnotationSupport; | |
89 | setCodeMiningProviders(codeMiningProviders); | |
90 | } | |
91 | ||
92 | /** | |
93 | * Set the codemining providers. | |
94 | * | |
95 | * @param codeMiningProviders the codemining providers. | |
96 | */ | |
97 | public void setCodeMiningProviders(ICodeMiningProvider[] codeMiningProviders) { | |
98 | cancel(); | |
99 | if (fCodeMiningProviders != null) { | |
100 | fCodeMiningProviders.stream().forEach(ICodeMiningProvider::dispose); | |
101 | } | |
102 | fCodeMiningProviders= Arrays.asList(codeMiningProviders); | |
103 | } | |
104 | ||
105 | /** | |
106 | * Uninstalls this codemining manager. | |
107 | */ | |
108 | public void uninstall() { | |
109 | cancel(); | |
110 | if (fInlinedAnnotationSupport != null) { | |
111 | fInlinedAnnotationSupport.updateAnnotations(Collections.emptySet()); | |
112 | } | |
113 | } | |
114 | ||
115 | /** | |
116 | * Collect, resolve and render the code minings of the viewer. | |
117 | */ | |
118 | @Override | |
119 | public void run() { | |
120 | if (fViewer == null || fInlinedAnnotationSupport == null || fCodeMiningProviders == null | |
121 | || fCodeMiningProviders.size() == 0 || fViewer.getAnnotationModel() == null) { | |
122 | return; | |
123 | } | |
124 | // Cancel the last progress monitor to cancel last resolve and render of code | |
125 | // minings | |
126 | cancel(); | |
127 | // Update the code minings | |
128 | updateCodeMinings(); | |
129 | } | |
130 | ||
131 | /** | |
132 | * Update the code minings. | |
133 | */ | |
134 | private void updateCodeMinings() { | |
135 | // Refresh the code minings by using the new progress monitor. | |
136 | fMonitor= new CancellationExceptionMonitor(); | |
137 | IProgressMonitor monitor= fMonitor; | |
138 | // Collect the code minings for the viewer | |
139 | getCodeMinings(fViewer, fCodeMiningProviders, monitor).thenAccept(symbols -> { | |
140 | // check if request was canceled. | |
141 | monitor.isCanceled(); | |
142 | // then group code minings by lines position | |
143 | Map<Position, List<ICodeMining>> groups= groupByLines(symbols, fCodeMiningProviders); | |
144 | // resolve and render code minings | |
145 | renderCodeMinings(groups, fViewer, monitor); | |
146 | }); | |
147 | } | |
148 | ||
149 | /** | |
150 | * Cancel the codemining process. | |
151 | */ | |
152 | private void cancel() { | |
153 | // Cancel the last progress monitor. | |
154 | if (fMonitor != null) { | |
155 | fMonitor.setCanceled(true); | |
156 | } | |
157 | } | |
158 | ||
159 | private static void logCodeMiningProviderException(Throwable e) { | |
160 | if (e != null && (e instanceof CancellationException || (e.getCause() != null && e.getCause() instanceof CancellationException))) { | |
161 | return; | |
162 | } | |
163 | String PLUGIN_ID= "org.eclipse.jface.text"; //$NON-NLS-1$ | |
164 | Bundle plugin= Platform.getBundle(PLUGIN_ID); | |
165 | if (plugin != null) { | |
166 | // In OSGi context, uses Platform Text log | |
167 | ILog log= Platform.getLog(plugin); | |
168 | log.log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, e.getMessage(), e)); | |
169 | } else { | |
170 | // In java main context, print stack trace | |
171 | System.err.println("Error while code mining process: " + e.getMessage()); //$NON-NLS-1$ | |
172 | } | |
173 | } | |
174 | ||
175 | /** | |
176 | * Return the list of {@link CompletableFuture} which provides the list of {@link ICodeMining} | |
177 | * for the given <code>viewer</code> by using the given providers. | |
178 | * | |
179 | * @param viewer the text viewer. | |
180 | * @param providers the CodeMining list providers. | |
181 | * @param monitor the progress monitor. | |
182 | * @return the list of {@link CompletableFuture} which provides the list of {@link ICodeMining} | |
183 | * for the given <code>viewer</code> by using the given providers. | |
184 | */ | |
185 | private static CompletableFuture<List<? extends ICodeMining>> getCodeMinings(ITextViewer viewer, | |
186 | List<ICodeMiningProvider> providers, IProgressMonitor monitor) { | |
187 | List<CompletableFuture<List<? extends ICodeMining>>> com= providers.stream() | |
188 | .map(provider -> provider.provideCodeMinings(viewer, monitor)) | |
189 | .filter(c -> c != null) | |
190 | .map(future -> future.exceptionally(e -> { | |
191 | logCodeMiningProviderException(e); | |
192 | return Collections.emptyList(); | |
193 | })) | |
194 | .collect(Collectors.toList()); | |
195 | return CompletableFuture.allOf(com.toArray(new CompletableFuture[com.size()])).thenApply( | |
196 | v -> com.stream().map(CompletableFuture::join).flatMap(l -> l.stream()).collect(Collectors.toList())); | |
197 | } | |
198 | ||
199 | /** | |
200 | * Returns a sorted Map which groups the given code minings by same position line. | |
201 | * | |
202 | * @param codeMinings list of code minings to group. | |
203 | * @param providers CodeMining providers used to retrieve code minings. | |
204 | * @return a sorted Map which groups the given code minings by same position line. | |
205 | */ | |
206 | private static Map<Position, List<ICodeMining>> groupByLines(List<? extends ICodeMining> codeMinings, | |
207 | List<ICodeMiningProvider> providers) { | |
208 | // sort code minings by lineNumber and provider-rank if | |
209 | Collections.sort(codeMinings, (a, b) -> { | |
210 | if (a.getPosition().offset < b.getPosition().offset) { | |
211 | return -1; | |
212 | } else if (a.getPosition().offset > b.getPosition().offset) { | |
213 | return 1; | |
214 | } else if (providers.indexOf(a.getProvider()) < providers.indexOf(b.getProvider())) { | |
215 | return -1; | |
216 | } else if (providers.indexOf(a.getProvider()) > providers.indexOf(b.getProvider())) { | |
217 | return 1; | |
218 | } else { | |
219 | return 0; | |
220 | } | |
221 | }); | |
222 | return codeMinings.stream().collect(Collectors.groupingBy(ICodeMining::getPosition, LinkedHashMap::new, | |
223 | Collectors.mapping(Function.identity(), Collectors.toList()))); | |
224 | } | |
225 | ||
226 | /** | |
227 | * Render the codemining grouped by line position. | |
228 | * | |
229 | * @param groups code minings grouped by lines position | |
230 | * @param viewer the viewer | |
231 | * @param monitor the progress monitor | |
232 | */ | |
233 | private void renderCodeMinings(Map<Position, List<ICodeMining>> groups, ISourceViewer viewer, | |
234 | IProgressMonitor monitor) { | |
235 | // check if request was canceled. | |
236 | monitor.isCanceled(); | |
237 | IDocument document= viewer != null ? viewer.getDocument() : null; | |
238 | if (document == null) { | |
239 | // this case comes from when editor is closed before codemining rendered is | |
240 | // done. | |
241 | return; | |
242 | } | |
243 | Set<ICodeMiningAnnotation> annotationsToRedraw= new HashSet<>(); | |
244 | Set<AbstractInlinedAnnotation> currentAnnotations= new HashSet<>(); | |
245 | // Loop for grouped code minings | |
246 | groups.entrySet().stream().forEach(g -> { | |
247 | // check if request was canceled. | |
248 | monitor.isCanceled(); | |
249 | ||
250 | Position pos= new Position(g.getKey().offset, g.getKey().length); | |
251 | List<ICodeMining> minings= g.getValue(); | |
252 | boolean inLineHeader= minings.size() > 0 ? (minings.get(0) instanceof LineHeaderCodeMining) : true; | |
253 | // Try to find existing annotation | |
254 | AbstractInlinedAnnotation ann= fInlinedAnnotationSupport.findExistingAnnotation(pos); | |
255 | if (ann == null) { | |
256 | // The annotation doesn't exists, create it. | |
257 | ann= inLineHeader ? new CodeMiningLineHeaderAnnotation(pos, viewer) : new CodeMiningLineContentAnnotation(pos, viewer); | |
258 | } else if (ann instanceof ICodeMiningAnnotation && ((ICodeMiningAnnotation) ann).isInVisibleLines()) { | |
259 | // annotation is in visible lines | |
260 | annotationsToRedraw.add((ICodeMiningAnnotation) ann); | |
261 | } | |
262 | ((ICodeMiningAnnotation) ann).update(minings, monitor); | |
263 | currentAnnotations.add(ann); | |
264 | }); | |
265 | // check if request was canceled. | |
266 | monitor.isCanceled(); | |
267 | fInlinedAnnotationSupport.updateAnnotations(currentAnnotations); | |
268 | // redraw the existing codemining annotations since their content can change | |
269 | annotationsToRedraw.stream().forEach(ann -> ann.redraw()); | |
270 | } | |
271 | ||
272 | /** | |
273 | * Returns <code>true</code> if the given mining has a non empty label and <code>false</code> | |
274 | * otherwise. | |
275 | * | |
276 | * @param mining the mining to check | |
277 | * @return <code>true</code> if the given mining has a non empty label and <code>false</code> | |
278 | * otherwise. | |
279 | */ | |
280 | static boolean isValidMining(ICodeMining mining) { | |
281 | return mining != null && mining.getLabel() != null && !mining.getLabel().isEmpty(); | |
282 | } | |
283 | ||
284 | /** | |
285 | * Returns the valid code mining at the given location by using the bounds of codemining | |
286 | * annotations which stores only the valid code mining. | |
287 | * | |
288 | * @param minings the list of mining of the codemining annotation. | |
289 | * @param bounds the bounds of the valid minings of the codemining annotation. | |
290 | * @param x the x location | |
291 | * @param y the y location | |
292 | * @return the valid code mining at the given location by using the bounds of codemining | |
293 | * annotations which stores only the valid code mining. | |
294 | */ | |
295 | static ICodeMining getValidCodeMiningAtLocation(ICodeMining[] minings, List<Rectangle> bounds, int x, int y) { | |
296 | for (int i= 0; i < bounds.size(); i++) { | |
297 | Rectangle bound= bounds.get(i); | |
298 | if (bound.contains(x, y)) { | |
299 | return getCodeValidMiningAtIndex(minings, i); | |
300 | } | |
301 | } | |
302 | return null; | |
303 | } | |
304 | ||
305 | /** | |
306 | * Returns the valid code mining at the given index. | |
307 | * | |
308 | * @param minings the list of minings | |
309 | * @param index the index | |
310 | * @return the valid code mining at the given index. | |
311 | */ | |
312 | private static ICodeMining getCodeValidMiningAtIndex(ICodeMining[] minings, int index) { | |
313 | int validIndex= 0; | |
314 | for (ICodeMining mining : minings) { | |
315 | if (isValidMining(mining)) { | |
316 | if (validIndex == index) { | |
317 | return mining; | |
318 | } | |
319 | validIndex++; | |
320 | } | |
321 | } | |
322 | return null; | |
323 | } | |
324 | } |
+1
-1
49 | 49 | /** |
50 | 50 | * @see Reader#ready() |
51 | 51 | */ |
52 | @Override | |
52 | @Override | |
53 | 53 | public boolean ready() throws IOException { |
54 | 54 | return true; |
55 | 55 | } |
+1
-1
130 | 130 | /** |
131 | 131 | * @see Reader#ready() |
132 | 132 | */ |
133 | @Override | |
133 | @Override | |
134 | 134 | public boolean ready() throws IOException { |
135 | 135 | return fReader.ready(); |
136 | 136 | } |
+1
-1
1540 | 1540 | * @since 3.4 |
1541 | 1541 | */ |
1542 | 1542 | boolean isColoredLabelsSupportEnabled() { |
1543 | return fIsColoredLabelsSupportEnabled; | |
1543 | return fIsColoredLabelsSupportEnabled; | |
1544 | 1544 | } |
1545 | 1545 | |
1546 | 1546 | /** |
+1
-1
563 | 563 | |
564 | 564 | } else if (key == SWT.ESC) { |
565 | 565 | e.doit= false; |
566 | hideContextInfoPopup(); | |
566 | hideContextInfoPopup(); | |
567 | 567 | } else { |
568 | 568 | validateContextInformation(); |
569 | 569 | } |
+1
-1
29 | 29 | super(); |
30 | 30 | } |
31 | 31 | |
32 | /** | |
32 | /** | |
33 | 33 | * Constructs an <code>LineIndexOutOfBoundsException</code> with the specified detail message. |
34 | 34 | * |
35 | 35 | * @param s the detail message. |
+63
-63
284 | 284 | updateFocusRevision(null); // kill any focus as the ctx menu is going to show |
285 | 285 | if (e.button == 1) { |
286 | 286 | fMouseDownRegion= fFocusRange; |
287 | postRedraw(); | |
287 | postRedraw(); | |
288 | 288 | } |
289 | 289 | } |
290 | 290 | |
350 | 350 | @Override |
351 | 351 | protected IInformationControl doCreateInformationControl(Shell parent) { |
352 | 352 | if (BrowserInformationControl.isAvailable(parent)) { |
353 | return new BrowserInformationControl(parent, JFaceResources.DIALOG_FONT, fIsFocusable) { | |
354 | /** | |
353 | return new BrowserInformationControl(parent, JFaceResources.DIALOG_FONT, fIsFocusable) { | |
354 | /** | |
355 | 355 | * {@inheritDoc} |
356 | 356 | * |
357 | 357 | * @deprecated use {@link #setInput(Object)} |
358 | 358 | */ |
359 | @Deprecated | |
359 | @Deprecated | |
360 | 360 | @Override |
361 | 361 | public void setInformation(String content) { |
362 | content= addCSSToHTMLFragment(content); | |
363 | super.setInformation(content); | |
364 | } | |
365 | ||
366 | /** | |
367 | * Adds a HTML header and CSS info if <code>html</code> is only an HTML fragment (has no | |
368 | * <html> section). | |
369 | * | |
370 | * @param html the html / text produced by a revision | |
371 | * @return modified html | |
372 | */ | |
373 | private String addCSSToHTMLFragment(String html) { | |
374 | int max= Math.min(100, html.length()); | |
375 | if (html.substring(0, max).indexOf("<html>") != -1) //$NON-NLS-1$ | |
376 | // there is already a header | |
377 | return html; | |
378 | ||
379 | StringBuilder info= new StringBuilder(512 + html.length()); | |
380 | HTMLPrinter.insertPageProlog(info, 0, fgStyleSheet); | |
381 | info.append(html); | |
382 | HTMLPrinter.addPageEpilog(info); | |
383 | return info.toString(); | |
384 | } | |
385 | ||
386 | }; | |
387 | } | |
362 | content= addCSSToHTMLFragment(content); | |
363 | super.setInformation(content); | |
364 | } | |
365 | ||
366 | /** | |
367 | * Adds a HTML header and CSS info if <code>html</code> is only an HTML fragment (has no | |
368 | * <html> section). | |
369 | * | |
370 | * @param html the html / text produced by a revision | |
371 | * @return modified html | |
372 | */ | |
373 | private String addCSSToHTMLFragment(String html) { | |
374 | int max= Math.min(100, html.length()); | |
375 | if (html.substring(0, max).indexOf("<html>") != -1) //$NON-NLS-1$ | |
376 | // there is already a header | |
377 | return html; | |
378 | ||
379 | StringBuilder info= new StringBuilder(512 + html.length()); | |
380 | HTMLPrinter.insertPageProlog(info, 0, fgStyleSheet); | |
381 | info.append(html); | |
382 | HTMLPrinter.addPageEpilog(info); | |
383 | return info.toString(); | |
384 | } | |
385 | ||
386 | }; | |
387 | } | |
388 | 388 | return new DefaultInformationControl(parent, fIsFocusable); |
389 | 389 | } |
390 | 390 | |
472 | 472 | return range == null ? null : new LineRange(lineNumber, 1); |
473 | 473 | } |
474 | 474 | |
475 | @Override | |
475 | @Override | |
476 | 476 | public IInformationControlCreator getInformationPresenterControlCreator() { |
477 | 477 | RevisionInformation revisionInfo= fRevisionInfo; |
478 | 478 | if (revisionInfo != null) { |
481 | 481 | return creator; |
482 | 482 | } |
483 | 483 | return new HoverInformationControlCreator(true); |
484 | } | |
484 | } | |
485 | 485 | } |
486 | 486 | |
487 | 487 | /* Listeners and helpers. */ |
1155 | 1155 | /** |
1156 | 1156 | * Handles the selection of a revision id and informs listeners |
1157 | 1157 | * |
1158 | * @param id the selected revision id | |
1159 | */ | |
1158 | * @param id the selected revision id | |
1159 | */ | |
1160 | 1160 | void handleRevisionSelected(String id) { |
1161 | 1161 | Assert.isLegal(id != null); |
1162 | 1162 | if (fRevisionInfo == null) |
1173 | 1173 | handleRevisionSelected((Revision) null); |
1174 | 1174 | } |
1175 | 1175 | |
1176 | /** | |
1177 | * Returns the selection provider. | |
1178 | * | |
1179 | * @return the selection provider | |
1180 | */ | |
1181 | public RevisionSelectionProvider getRevisionSelectionProvider() { | |
1176 | /** | |
1177 | * Returns the selection provider. | |
1178 | * | |
1179 | * @return the selection provider | |
1180 | */ | |
1181 | public RevisionSelectionProvider getRevisionSelectionProvider() { | |
1182 | 1182 | return fRevisionSelectionProvider; |
1183 | } | |
1183 | } | |
1184 | 1184 | |
1185 | 1185 | /** |
1186 | 1186 | * Updates the focus line with a new line. |
1231 | 1231 | } |
1232 | 1232 | |
1233 | 1233 | private void updateFocusRevision(Revision revision) { |
1234 | if (fFocusRevision != revision) | |
1234 | if (fFocusRevision != revision) | |
1235 | 1235 | onFocusRevisionChanged(fFocusRevision, revision); |
1236 | } | |
1236 | } | |
1237 | 1237 | |
1238 | 1238 | /** |
1239 | 1239 | * Handles a changing focus revision. |
1422 | 1422 | * @param offset the document offset |
1423 | 1423 | * @return the revision at offset, or <code>null</code> for none |
1424 | 1424 | */ |
1425 | Revision getRevision(int offset) { | |
1426 | IDocument document= fViewer.getDocument(); | |
1427 | int line; | |
1428 | try { | |
1429 | line= document.getLineOfOffset(offset); | |
1430 | } catch (BadLocationException x) { | |
1431 | return null; | |
1432 | } | |
1433 | if (line != -1) { | |
1434 | RevisionRange range= getRange(line); | |
1435 | if (range != null) | |
1436 | return range.getRevision(); | |
1437 | } | |
1438 | return null; | |
1439 | } | |
1425 | Revision getRevision(int offset) { | |
1426 | IDocument document= fViewer.getDocument(); | |
1427 | int line; | |
1428 | try { | |
1429 | line= document.getLineOfOffset(offset); | |
1430 | } catch (BadLocationException x) { | |
1431 | return null; | |
1432 | } | |
1433 | if (line != -1) { | |
1434 | RevisionRange range= getRange(line); | |
1435 | if (range != null) | |
1436 | return range.getRevision(); | |
1437 | } | |
1438 | return null; | |
1439 | } | |
1440 | 1440 | |
1441 | 1441 | /** |
1442 | 1442 | * Returns <code>true</code> if a revision model has been set, <code>false</code> otherwise. |
1443 | 1443 | * |
1444 | * @return <code>true</code> if a revision model has been set, <code>false</code> otherwise | |
1445 | */ | |
1446 | public boolean hasInformation() { | |
1447 | return fRevisionInfo != null; | |
1448 | } | |
1444 | * @return <code>true</code> if a revision model has been set, <code>false</code> otherwise | |
1445 | */ | |
1446 | public boolean hasInformation() { | |
1447 | return fRevisionInfo != null; | |
1448 | } | |
1449 | 1449 | |
1450 | 1450 | /** |
1451 | 1451 | * Returns the width in chars required to display information. |
+63
-63
45 | 45 | * Post selection listener on the viewer that remembers the selection provider it is registered |
46 | 46 | * with. |
47 | 47 | */ |
48 | private final class PostSelectionListener implements ISelectionChangedListener { | |
49 | private final IPostSelectionProvider fPostProvider; | |
48 | private final class PostSelectionListener implements ISelectionChangedListener { | |
49 | private final IPostSelectionProvider fPostProvider; | |
50 | 50 | |
51 | 51 | public PostSelectionListener(IPostSelectionProvider postProvider) { |
52 | 52 | postProvider.addPostSelectionChangedListener(this); |
53 | 53 | fPostProvider= postProvider; |
54 | } | |
54 | } | |
55 | 55 | |
56 | 56 | @Override |
57 | 57 | public void selectionChanged(SelectionChangedEvent event) { |
58 | ISelection selection= event.getSelection(); | |
59 | if (selection instanceof ITextSelection) { | |
60 | ITextSelection ts= (ITextSelection) selection; | |
61 | int offset= ts.getOffset(); | |
62 | setSelectedRevision(fPainter.getRevision(offset)); | |
63 | } | |
58 | ISelection selection= event.getSelection(); | |
59 | if (selection instanceof ITextSelection) { | |
60 | ITextSelection ts= (ITextSelection) selection; | |
61 | int offset= ts.getOffset(); | |
62 | setSelectedRevision(fPainter.getRevision(offset)); | |
63 | } | |
64 | 64 | |
65 | } | |
65 | } | |
66 | 66 | |
67 | 67 | public void dispose() { |
68 | 68 | fPostProvider.removePostSelectionChangedListener(this); |
69 | 69 | } |
70 | } | |
70 | } | |
71 | 71 | |
72 | 72 | private final RevisionPainter fPainter; |
73 | 73 | |
97 | 97 | * |
98 | 98 | * @param painter the painter that the created provider interacts with |
99 | 99 | */ |
100 | RevisionSelectionProvider(RevisionPainter painter) { | |
100 | RevisionSelectionProvider(RevisionPainter painter) { | |
101 | 101 | fPainter= painter; |
102 | } | |
102 | } | |
103 | 103 | |
104 | @Override | |
104 | @Override | |
105 | 105 | public void addSelectionChangedListener(ISelectionChangedListener listener) { |
106 | fListeners.add(listener); | |
107 | } | |
106 | fListeners.add(listener); | |
107 | } | |
108 | 108 | |
109 | @Override | |
109 | @Override | |
110 | 110 | public void removeSelectionChangedListener(ISelectionChangedListener listener) { |
111 | fListeners.remove(listener); | |
112 | } | |
111 | fListeners.remove(listener); | |
112 | } | |
113 | 113 | |
114 | @Override | |
114 | @Override | |
115 | 115 | public ISelection getSelection() { |
116 | if (fSelection == null) | |
117 | return StructuredSelection.EMPTY; | |
118 | return new StructuredSelection(fSelection); | |
119 | } | |
116 | if (fSelection == null) | |
117 | return StructuredSelection.EMPTY; | |
118 | return new StructuredSelection(fSelection); | |
119 | } | |
120 | 120 | |
121 | @Override | |
121 | @Override | |
122 | 122 | public void setSelection(ISelection selection) { |
123 | if (fIgnoreEvents) | |
124 | return; | |
125 | if (selection instanceof IStructuredSelection) { | |
126 | Object first= ((IStructuredSelection) selection).getFirstElement(); | |
127 | if (first instanceof Revision) | |
123 | if (fIgnoreEvents) | |
124 | return; | |
125 | if (selection instanceof IStructuredSelection) { | |
126 | Object first= ((IStructuredSelection) selection).getFirstElement(); | |
127 | if (first instanceof Revision) | |
128 | 128 | fPainter.handleRevisionSelected((Revision) first); |
129 | 129 | else if (first instanceof String) |
130 | 130 | fPainter.handleRevisionSelected((String) first); |
131 | 131 | else if (selection.isEmpty()) |
132 | 132 | fPainter.handleRevisionSelected((Revision) null); |
133 | } | |
134 | } | |
133 | } | |
134 | } | |
135 | 135 | |
136 | /** | |
136 | /** | |
137 | 137 | * Installs the selection provider on the viewer. |
138 | 138 | * |
139 | 139 | * @param viewer the viewer on which we listen to for post selection events |
140 | 140 | */ |
141 | void install(ITextViewer viewer) { | |
142 | uninstall(); | |
141 | void install(ITextViewer viewer) { | |
142 | uninstall(); | |
143 | 143 | fViewer= viewer; |
144 | 144 | if (fViewer != null) { |
145 | 145 | ISelectionProvider provider= fViewer.getSelectionProvider(); |
146 | 146 | if (provider instanceof IPostSelectionProvider) { |
147 | IPostSelectionProvider postProvider= (IPostSelectionProvider) provider; | |
148 | fSelectionListener= new PostSelectionListener(postProvider); | |
149 | } | |
147 | IPostSelectionProvider postProvider= (IPostSelectionProvider) provider; | |
148 | fSelectionListener= new PostSelectionListener(postProvider); | |
149 | } | |
150 | 150 | } |
151 | } | |
151 | } | |
152 | 152 | |
153 | /** | |
154 | * Uninstalls the selection provider. | |
155 | */ | |
156 | void uninstall() { | |
157 | fViewer= null; | |
158 | if (fSelectionListener != null) { | |
159 | fSelectionListener.dispose(); | |
160 | fSelectionListener= null; | |
161 | } | |
162 | } | |
153 | /** | |
154 | * Uninstalls the selection provider. | |
155 | */ | |
156 | void uninstall() { | |
157 | fViewer= null; | |
158 | if (fSelectionListener != null) { | |
159 | fSelectionListener.dispose(); | |
160 | fSelectionListener= null; | |
161 | } | |
162 | } | |
163 | 163 | |
164 | /** | |
164 | /** | |
165 | 165 | * Private protocol used by {@link RevisionPainter} to signal selection of a revision. |
166 | 166 | * |
167 | 167 | * @param revision the selected revision, or <code>null</code> for none |
168 | 168 | */ |
169 | void revisionSelected(Revision revision) { | |
170 | setSelectedRevision(revision); | |
171 | } | |
169 | void revisionSelected(Revision revision) { | |
170 | setSelectedRevision(revision); | |
171 | } | |
172 | 172 | |
173 | 173 | /** |
174 | 174 | * Updates the currently selected revision and sends out an event if it changed. |
180 | 180 | fSelection= revision; |
181 | 181 | fireSelectionEvent(); |
182 | 182 | } |
183 | } | |
183 | } | |
184 | 184 | |
185 | private void fireSelectionEvent() { | |
186 | fIgnoreEvents= true; | |
187 | try { | |
188 | ISelection selection= getSelection(); | |
189 | SelectionChangedEvent event= new SelectionChangedEvent(this, selection); | |
185 | private void fireSelectionEvent() { | |
186 | fIgnoreEvents= true; | |
187 | try { | |
188 | ISelection selection= getSelection(); | |
189 | SelectionChangedEvent event= new SelectionChangedEvent(this, selection); | |
190 | 190 | |
191 | 191 | for (ISelectionChangedListener listener : fListeners) { |
192 | 192 | listener.selectionChanged(event); |
193 | 193 | } |
194 | } finally { | |
195 | fIgnoreEvents= false; | |
196 | } | |
197 | } | |
194 | } finally { | |
195 | fIgnoreEvents= false; | |
196 | } | |
197 | } | |
198 | 198 | } |
525 | 525 | /** |
526 | 526 | * Returns <code>true</code> if diff information is being displayed, <code>false</code> otherwise. |
527 | 527 | * |
528 | * @return <code>true</code> if diff information is being displayed, <code>false</code> otherwise | |
529 | * @since 3.3 | |
530 | */ | |
528 | * @return <code>true</code> if diff information is being displayed, <code>false</code> otherwise | |
529 | * @since 3.3 | |
530 | */ | |
531 | 531 | public boolean hasInformation() { |
532 | 532 | if (fLineDiffer instanceof ILineDifferExtension2) |
533 | 533 | return !((ILineDifferExtension2) fLineDiffer).isSuspended(); |
+4
-4
608 | 608 | private MouseTracker fMouseTracker= new MouseTracker(); |
609 | 609 | /** |
610 | 610 | * The remembered hover event. |
611 | * @since 3.0 | |
611 | * @since 3.0 | |
612 | 612 | */ |
613 | 613 | private MouseEvent fHoverEvent= null; |
614 | 614 | /** The remembered hover event state mask of the keyboard modifiers */ |
844 | 844 | fReplacingDelayJob= new Job("AbstractHoverInformationControlManager Replace Delayer") { //$NON-NLS-1$ |
845 | 845 | @Override |
846 | 846 | public IStatus run(final IProgressMonitor monitor) { |
847 | if (monitor.isCanceled() || display.isDisposed()) { | |
847 | if (monitor.isCanceled() || display.isDisposed()) { | |
848 | 848 | return Status.CANCEL_STATUS; |
849 | 849 | } |
850 | 850 | display.syncExec(() -> { |
938 | 938 | return fHoverEvent; |
939 | 939 | } |
940 | 940 | |
941 | /** | |
941 | /** | |
942 | 942 | * Returns the SWT event state of the most recent mouse hover event. |
943 | 943 | * |
944 | 944 | * @return the SWT event state of the most recent mouse hover event |
945 | 945 | */ |
946 | 946 | protected int getHoverEventStateMask() { |
947 | 947 | return fHoverEventStateMask; |
948 | } | |
948 | } | |
949 | 949 | |
950 | 950 | /** |
951 | 951 | * Returns an adapter that gives access to internal methods. |
351 | 351 | return; |
352 | 352 | |
353 | 353 | try { |
354 | IDocument document= fEvent.getDocument(); | |
355 | if (document == null) | |
356 | return; | |
354 | IDocument document= fEvent.getDocument(); | |
355 | if (document == null) | |
356 | return; | |
357 | 357 | |
358 | 358 | TextChangingEvent event= new TextChangingEvent(this); |
359 | 359 | event.start= fEvent.fOffset; |
120 | 120 | /** |
121 | 121 | * Additional styles to use for the text control. |
122 | 122 | * @since 3.4, previously called <code>fTextStyle</code> |
123 | */ | |
123 | */ | |
124 | 124 | private final int fAdditionalTextStyles; |
125 | 125 | |
126 | 126 | /** |
+2
-2
74 | 74 | |
75 | 75 | @Override |
76 | 76 | public char last() { |
77 | fIndex= fOffset < fEndOffset ? fEndOffset -1 : fEndOffset; | |
78 | return current(); | |
77 | fIndex= fOffset < fEndOffset ? fEndOffset -1 : fEndOffset; | |
78 | return current(); | |
79 | 79 | } |
80 | 80 | |
81 | 81 | @Override |
103 | 103 | * @since 3.1 |
104 | 104 | */ |
105 | 105 | TextCommand(IUndoContext context) { |
106 | super(JFaceTextMessages.getString("DefaultUndoManager.operationLabel")); //$NON-NLS-1$ | |
107 | addContext(context); | |
106 | super(JFaceTextMessages.getString("DefaultUndoManager.operationLabel")); //$NON-NLS-1$ | |
107 | addContext(context); | |
108 | 108 | } |
109 | 109 | |
110 | 110 | /** |
132 | 132 | |
133 | 133 | @Override |
134 | 134 | public void dispose() { |
135 | reinitialize(); | |
135 | reinitialize(); | |
136 | 136 | } |
137 | 137 | |
138 | 138 | /** |
222 | 222 | |
223 | 223 | @Override |
224 | 224 | public boolean canExecute() { |
225 | return isConnected(); | |
225 | return isConnected(); | |
226 | 226 | } |
227 | 227 | |
228 | 228 | @Override |
229 | 229 | public IStatus execute(IProgressMonitor monitor, IAdaptable uiInfo) { |
230 | 230 | // Text commands execute as they are typed, so executing one has no effect. |
231 | return Status.OK_STATUS; | |
231 | return Status.OK_STATUS; | |
232 | 232 | } |
233 | 233 | |
234 | 234 | /* |
323 | 323 | } else { |
324 | 324 | reinitialize(); |
325 | 325 | } |
326 | } else { | |
326 | } else { | |
327 | 327 | updateCommand(); |
328 | 328 | fCurrent= createCurrent(); |
329 | 329 | } |
367 | 367 | * @since 3.1 |
368 | 368 | */ |
369 | 369 | protected boolean isValid() { |
370 | return fStart > -1 && | |
371 | fEnd > -1 && | |
372 | fText != null; | |
370 | return fStart > -1 && | |
371 | fEnd > -1 && | |
372 | fText != null; | |
373 | 373 | } |
374 | 374 | |
375 | 375 | @Override |
376 | 376 | public String toString() { |
377 | 377 | String delimiter= ", "; //$NON-NLS-1$ |
378 | StringBuilder text= new StringBuilder(super.toString()); | |
378 | StringBuilder text= new StringBuilder(super.toString()); | |
379 | 379 | text.append("\n"); //$NON-NLS-1$ |
380 | 380 | text.append(this.getClass().getName()); |
381 | 381 | text.append(" undo modification stamp: "); //$NON-NLS-1$ |
385 | 385 | text.append(" start: "); //$NON-NLS-1$ |
386 | 386 | text.append(fStart); |
387 | 387 | text.append(delimiter); |
388 | text.append("end: "); //$NON-NLS-1$ | |
389 | text.append(fEnd); | |
388 | text.append("end: "); //$NON-NLS-1$ | |
389 | text.append(fEnd); | |
390 | 390 | text.append(delimiter); |
391 | text.append("text: '"); //$NON-NLS-1$ | |
391 | text.append("text: '"); //$NON-NLS-1$ | |
392 | 392 | text.append(fText); |
393 | text.append('\''); | |
393 | text.append('\''); | |
394 | 394 | text.append(delimiter); |
395 | text.append("preservedText: '"); //$NON-NLS-1$ | |
395 | text.append("preservedText: '"); //$NON-NLS-1$ | |
396 | 396 | text.append(fPreservedText); |
397 | text.append('\''); | |
398 | return text.toString(); | |
397 | text.append('\''); | |
398 | return text.toString(); | |
399 | 399 | } |
400 | 400 | |
401 | 401 | /** |
435 | 435 | * @since 3.1 |
436 | 436 | */ |
437 | 437 | CompoundTextCommand(IUndoContext context) { |
438 | super(context); | |
438 | super(context); | |
439 | 439 | } |
440 | 440 | |
441 | 441 | /** |
544 | 544 | protected boolean isValid() { |
545 | 545 | if (isConnected()) |
546 | 546 | return (fStart > -1 || fCommands.size() > 0); |
547 | return false; | |
547 | return false; | |
548 | 548 | } |
549 | 549 | |
550 | 550 | /** |
826 | 826 | * @param undoLevel the length of this manager's history |
827 | 827 | */ |
828 | 828 | public DefaultUndoManager(int undoLevel) { |
829 | fHistory= OperationHistoryFactory.getOperationHistory(); | |
829 | fHistory= OperationHistoryFactory.getOperationHistory(); | |
830 | 830 | setMaximalUndoLevel(undoLevel); |
831 | 831 | } |
832 | 832 | |
908 | 908 | */ |
909 | 909 | private void addToCommandStack(TextCommand command){ |
910 | 910 | if (!fFoldingIntoCompoundChange || command instanceof CompoundTextCommand) { |
911 | fHistory.add(command); | |
912 | fLastAddedCommand= command; | |
911 | fHistory.add(command); | |
912 | fLastAddedCommand= command; | |
913 | 913 | } |
914 | 914 | } |
915 | 915 | |
919 | 919 | * @since 3.1 |
920 | 920 | */ |
921 | 921 | private void disposeCommandStack() { |
922 | fHistory.dispose(fUndoContext, true, true, true); | |
922 | fHistory.dispose(fUndoContext, true, true, true); | |
923 | 923 | } |
924 | 924 | |
925 | 925 | /** |
928 | 928 | * @since 3.1 |
929 | 929 | */ |
930 | 930 | private void initializeCommandStack() { |
931 | if (fHistory != null && fUndoContext != null) | |
931 | if (fHistory != null && fUndoContext != null) | |
932 | 932 | fHistory.dispose(fUndoContext, true, true, false); |
933 | 933 | |
934 | 934 | } |
1192 | 1192 | fTextViewer= textViewer; |
1193 | 1193 | fTextBuffer= new StringBuilder(); |
1194 | 1194 | fPreservedTextBuffer= new StringBuilder(); |
1195 | if (fUndoContext == null) | |
1196 | fUndoContext= new ObjectUndoContext(this); | |
1197 | ||
1198 | fHistory.setLimit(fUndoContext, fUndoLevel); | |
1195 | if (fUndoContext == null) | |
1196 | fUndoContext= new ObjectUndoContext(this); | |
1197 | ||
1198 | fHistory.setLimit(fUndoContext, fUndoLevel); | |
1199 | 1199 | |
1200 | 1200 | initializeCommandStack(); |
1201 | 1201 | |
1239 | 1239 | |
1240 | 1240 | @Override |
1241 | 1241 | public boolean redoable() { |
1242 | return fHistory.canRedo(fUndoContext); | |
1242 | return fHistory.canRedo(fUndoContext); | |
1243 | 1243 | } |
1244 | 1244 | |
1245 | 1245 | @Override |
1246 | 1246 | public boolean undoable() { |
1247 | return fHistory.canUndo(fUndoContext); | |
1247 | return fHistory.canUndo(fUndoContext); | |
1248 | 1248 | } |
1249 | 1249 | |
1250 | 1250 | @Override |
70 | 70 | public DocumentClone(String content, String[] lineDelimiters) { |
71 | 71 | super(); |
72 | 72 | setTextStore(new StringTextStore(content)); |
73 | ConfigurableLineTracker tracker= new ConfigurableLineTracker(lineDelimiters); | |
73 | ||
74 | boolean hasDefaultDelims= lineDelimiters == null; | |
75 | if (!hasDefaultDelims && DefaultLineTracker.DELIMITERS.length == lineDelimiters.length) { | |
76 | hasDefaultDelims= true; | |
77 | for (int i= 0; i < lineDelimiters.length; i++) { | |
78 | if (DefaultLineTracker.DELIMITERS[i] != lineDelimiters[i]) { | |
79 | hasDefaultDelims= false; | |
80 | break; | |
81 | } | |
82 | } | |
83 | } | |
84 | ||
85 | ILineTracker tracker= hasDefaultDelims ? new DefaultLineTracker() : new ConfigurableLineTracker(lineDelimiters); | |
74 | 86 | setLineTracker(tracker); |
75 | 87 | getTracker().set(content); |
76 | 88 | completeInitialization(); |
54 | 54 | */ |
55 | 55 | boolean canPerformFind(); |
56 | 56 | |
57 | /** | |
58 | * Searches for a string starting at the given widget offset and using the specified search | |
59 | * directives. If a string has been found it is selected and its start offset is | |
60 | * returned. | |
61 | * <p> | |
62 | * Replaced by {@link IFindReplaceTargetExtension3#findAndSelect(int, String, boolean, boolean, boolean, boolean)}. | |
63 | * | |
64 | * @param widgetOffset the widget offset at which searching starts | |
65 | * @param findString the string which should be found | |
66 | * @param searchForward <code>true</code> searches forward, <code>false</code> backwards | |
67 | * @param caseSensitive <code>true</code> performs a case sensitive search, <code>false</code> an insensitive search | |
68 | * @param wholeWord if <code>true</code> only occurrences are reported in which the findString stands as a word by itself | |
69 | * @return the position of the specified string, or -1 if the string has not been found | |
57 | /** | |
58 | * Searches for a string starting at the given widget offset and using the specified search | |
59 | * directives. If a string has been found it is selected and its start offset is | |
60 | * returned. | |
61 | * <p> | |
62 | * Replaced by {@link IFindReplaceTargetExtension3#findAndSelect(int, String, boolean, boolean, boolean, boolean)}. | |
63 | * | |
64 | * @param widgetOffset the widget offset at which searching starts | |
65 | * @param findString the string which should be found | |
66 | * @param searchForward <code>true</code> searches forward, <code>false</code> backwards | |
67 | * @param caseSensitive <code>true</code> performs a case sensitive search, <code>false</code> an insensitive search | |
68 | * @param wholeWord if <code>true</code> only occurrences are reported in which the findString stands as a word by itself | |
69 | * @return the position of the specified string, or -1 if the string has not been found | |
70 | 70 | */ |
71 | 71 | int findAndSelect(int widgetOffset, String findString, boolean searchForward, boolean caseSensitive, boolean wholeWord); |
72 | 72 |
193 | 193 | * behavior over the course of time, this method causes them to be set |
194 | 194 | * back to their initial state and behavior. E.g., if an {@link IUndoManager} |
195 | 195 | * has been installed on this text viewer, the manager's list of remembered |
196 | * text editing operations is removed. | |
196 | * text editing operations is removed. | |
197 | 197 | */ |
198 | 198 | void resetPlugins(); |
199 | 199 |
46 | 46 | */ |
47 | 47 | public interface ITextViewerExtension2 { |
48 | 48 | |
49 | /** | |
50 | * The state mask of the default hover (value <code>0xff</code>). | |
51 | */ | |
49 | /** | |
50 | * The state mask of the default hover (value <code>0xff</code>). | |
51 | */ | |
52 | 52 | final int DEFAULT_HOVER_STATE_MASK= 0xff; |
53 | 53 | |
54 | 54 | /** |
2215 | 2215 | return true; |
2216 | 2216 | } |
2217 | 2217 | |
2218 | } else { | |
2219 | fWidgetTokenKeeper= requester; | |
2220 | return true; | |
2221 | } | |
2222 | } | |
2223 | return false; | |
2224 | } | |
2218 | } else { | |
2219 | fWidgetTokenKeeper= requester; | |
2220 | return true; | |
2221 | } | |
2222 | } | |
2223 | return false; | |
2224 | } | |
2225 | 2225 | |
2226 | 2226 | @Override |
2227 | 2227 | public void releaseWidgetToken(IWidgetTokenKeeper tokenKeeper) { |
2842 | 2842 | * @return <code>true</code> if the slave has been adapted successfully |
2843 | 2843 | * @throws BadLocationException in case the specified range is not valid in the master document |
2844 | 2844 | * @since 2.1 |
2845 | * @deprecated use <code>updateSlaveDocument</code> instead | |
2845 | * @deprecated use <code>updateSlaveDocument</code> instead | |
2846 | 2846 | */ |
2847 | 2847 | @Deprecated |
2848 | 2848 | protected boolean updateVisibleDocument(IDocument visibleDocument, int visibleRegionOffset, int visibleRegionLength) throws BadLocationException { |
4411 | 4411 | options.printTextFontStyle= true; |
4412 | 4412 | options.printTextForeground= true; |
4413 | 4413 | print(options); |
4414 | } | |
4414 | } | |
4415 | 4415 | |
4416 | 4416 | //------ find support |
4417 | 4417 |
+151
-151
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import java.util.concurrent.CompletableFuture; | |
16 | import java.util.function.Consumer; | |
17 | ||
18 | import org.eclipse.swt.custom.StyledText; | |
19 | import org.eclipse.swt.events.MouseEvent; | |
20 | import org.eclipse.swt.graphics.Color; | |
21 | import org.eclipse.swt.graphics.GC; | |
22 | import org.eclipse.swt.graphics.Point; | |
23 | ||
24 | import org.eclipse.core.runtime.IProgressMonitor; | |
25 | ||
26 | import org.eclipse.jface.text.ITextViewer; | |
27 | import org.eclipse.jface.text.Position; | |
28 | ||
29 | /** | |
30 | * Abstract class for {@link ICodeMining}. | |
31 | * | |
32 | * @since 3.13 | |
33 | */ | |
34 | public abstract class AbstractCodeMining implements ICodeMining { | |
35 | ||
36 | /** | |
37 | * The position where codemining must be drawn | |
38 | */ | |
39 | private final Position position; | |
40 | ||
41 | /** | |
42 | * The owner codemining provider which creates this mining. | |
43 | */ | |
44 | private final ICodeMiningProvider provider; | |
45 | ||
46 | /** | |
47 | * The future used to resolve mining. | |
48 | */ | |
49 | private CompletableFuture<Void> resolveFuture; | |
50 | ||
51 | /** | |
52 | * The label of the resolved codemining. | |
53 | */ | |
54 | private String label; | |
55 | ||
56 | /** | |
57 | * The action to execute when mining is clicked and null otherwise. | |
58 | */ | |
59 | private final Consumer<MouseEvent> action; | |
60 | ||
61 | /** | |
62 | * CodeMining constructor to locate the code mining in a given position. | |
63 | * | |
64 | * @param position the position where the mining must be drawn. | |
65 | * @param provider the owner codemining provider which creates this mining. | |
66 | * @param action the action to execute when mining is clicked and null otherwise. | |
67 | */ | |
68 | protected AbstractCodeMining(Position position, ICodeMiningProvider provider, Consumer<MouseEvent> action) { | |
69 | this.position= position; | |
70 | this.provider= provider; | |
71 | this.action= action; | |
72 | } | |
73 | ||
74 | @Override | |
75 | public Position getPosition() { | |
76 | return position; | |
77 | } | |
78 | ||
79 | @Override | |
80 | public ICodeMiningProvider getProvider() { | |
81 | return provider; | |
82 | } | |
83 | ||
84 | @Override | |
85 | public String getLabel() { | |
86 | return label; | |
87 | } | |
88 | ||
89 | /** | |
90 | * Set the label mining. | |
91 | * | |
92 | * @param label the label mining. | |
93 | */ | |
94 | public void setLabel(String label) { | |
95 | this.label= label; | |
96 | } | |
97 | ||
98 | @Override | |
99 | public final CompletableFuture<Void> resolve(ITextViewer viewer, IProgressMonitor monitor) { | |
100 | if (resolveFuture == null) { | |
101 | resolveFuture= doResolve(viewer, monitor); | |
102 | } | |
103 | return resolveFuture; | |
104 | } | |
105 | ||
106 | /** | |
107 | * Returns the future which resolved the content of mining and null otherwise. By default, the | |
108 | * resolve do nothing. | |
109 | * | |
110 | * @param viewer the viewer | |
111 | * @param monitor the monitor | |
112 | * @return the future which resolved the content of mining and null otherwise. | |
113 | */ | |
114 | protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { | |
115 | return CompletableFuture.completedFuture(null); | |
116 | } | |
117 | ||
118 | @Override | |
119 | public boolean isResolved() { | |
120 | return (resolveFuture != null && resolveFuture.isDone()); | |
121 | } | |
122 | ||
123 | @Override | |
124 | public void dispose() { | |
125 | if (resolveFuture != null) { | |
126 | resolveFuture.cancel(true); | |
127 | resolveFuture= null; | |
128 | } | |
129 | } | |
130 | ||
131 | /** | |
132 | * Draw the {@link #getLabel()} of mining with gray color. User can override this method to draw | |
133 | * anything. | |
134 | * | |
135 | * @param gc the graphics context | |
136 | * @param textWidget the text widget to draw on | |
137 | * @param color the color of the line | |
138 | * @param x the x position of the annotation | |
139 | * @param y the y position of the annotation | |
140 | * @return the size of the draw of mining. | |
141 | */ | |
142 | @Override | |
143 | public Point draw(GC gc, StyledText textWidget, Color color, int x, int y) { | |
144 | String title= getLabel() != null ? getLabel() : "no command"; //$NON-NLS-1$ | |
145 | gc.drawString(title, x, y, true); | |
146 | return gc.stringExtent(title); | |
147 | } | |
148 | ||
149 | @Override | |
150 | public Consumer<MouseEvent> getAction() { | |
151 | return action; | |
152 | } | |
153 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import java.util.concurrent.CompletableFuture; | |
16 | import java.util.function.Consumer; | |
17 | ||
18 | import org.eclipse.swt.custom.StyledText; | |
19 | import org.eclipse.swt.events.MouseEvent; | |
20 | import org.eclipse.swt.graphics.Color; | |
21 | import org.eclipse.swt.graphics.GC; | |
22 | import org.eclipse.swt.graphics.Point; | |
23 | ||
24 | import org.eclipse.core.runtime.IProgressMonitor; | |
25 | ||
26 | import org.eclipse.jface.text.ITextViewer; | |
27 | import org.eclipse.jface.text.Position; | |
28 | ||
29 | /** | |
30 | * Abstract class for {@link ICodeMining}. | |
31 | * | |
32 | * @since 3.13 | |
33 | */ | |
34 | public abstract class AbstractCodeMining implements ICodeMining { | |
35 | ||
36 | /** | |
37 | * The position where codemining must be drawn | |
38 | */ | |
39 | private final Position position; | |
40 | ||
41 | /** | |
42 | * The owner codemining provider which creates this mining. | |
43 | */ | |
44 | private final ICodeMiningProvider provider; | |
45 | ||
46 | /** | |
47 | * The future used to resolve mining. | |
48 | */ | |
49 | private CompletableFuture<Void> resolveFuture; | |
50 | ||
51 | /** | |
52 | * The label of the resolved codemining. | |
53 | */ | |
54 | private String label; | |
55 | ||
56 | /** | |
57 | * The action to execute when mining is clicked and null otherwise. | |
58 | */ | |
59 | private final Consumer<MouseEvent> action; | |
60 | ||
61 | /** | |
62 | * CodeMining constructor to locate the code mining in a given position. | |
63 | * | |
64 | * @param position the position where the mining must be drawn. | |
65 | * @param provider the owner codemining provider which creates this mining. | |
66 | * @param action the action to execute when mining is clicked and null otherwise. | |
67 | */ | |
68 | protected AbstractCodeMining(Position position, ICodeMiningProvider provider, Consumer<MouseEvent> action) { | |
69 | this.position= position; | |
70 | this.provider= provider; | |
71 | this.action= action; | |
72 | } | |
73 | ||
74 | @Override | |
75 | public Position getPosition() { | |
76 | return position; | |
77 | } | |
78 | ||
79 | @Override | |
80 | public ICodeMiningProvider getProvider() { | |
81 | return provider; | |
82 | } | |
83 | ||
84 | @Override | |
85 | public String getLabel() { | |
86 | return label; | |
87 | } | |
88 | ||
89 | /** | |
90 | * Set the label mining. | |
91 | * | |
92 | * @param label the label mining. | |
93 | */ | |
94 | public void setLabel(String label) { | |
95 | this.label= label; | |
96 | } | |
97 | ||
98 | @Override | |
99 | public final CompletableFuture<Void> resolve(ITextViewer viewer, IProgressMonitor monitor) { | |
100 | if (resolveFuture == null) { | |
101 | resolveFuture= doResolve(viewer, monitor); | |
102 | } | |
103 | return resolveFuture; | |
104 | } | |
105 | ||
106 | /** | |
107 | * Returns the future which resolved the content of mining and null otherwise. By default, the | |
108 | * resolve do nothing. | |
109 | * | |
110 | * @param viewer the viewer | |
111 | * @param monitor the monitor | |
112 | * @return the future which resolved the content of mining and null otherwise. | |
113 | */ | |
114 | protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { | |
115 | return CompletableFuture.completedFuture(null); | |
116 | } | |
117 | ||
118 | @Override | |
119 | public boolean isResolved() { | |
120 | return (resolveFuture != null && resolveFuture.isDone()); | |
121 | } | |
122 | ||
123 | @Override | |
124 | public void dispose() { | |
125 | if (resolveFuture != null) { | |
126 | resolveFuture.cancel(true); | |
127 | resolveFuture= null; | |
128 | } | |
129 | } | |
130 | ||
131 | /** | |
132 | * Draw the {@link #getLabel()} of mining with gray color. User can override this method to draw | |
133 | * anything. | |
134 | * | |
135 | * @param gc the graphics context | |
136 | * @param textWidget the text widget to draw on | |
137 | * @param color the color of the line | |
138 | * @param x the x position of the annotation | |
139 | * @param y the y position of the annotation | |
140 | * @return the size of the draw of mining. | |
141 | */ | |
142 | @Override | |
143 | public Point draw(GC gc, StyledText textWidget, Color color, int x, int y) { | |
144 | String title= getLabel() != null ? getLabel() : "no command"; //$NON-NLS-1$ | |
145 | gc.drawString(title, x, y, true); | |
146 | return gc.stringExtent(title); | |
147 | } | |
148 | ||
149 | @Override | |
150 | public Consumer<MouseEvent> getAction() { | |
151 | return action; | |
152 | } | |
153 | } |
+65
-65
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import org.eclipse.core.runtime.Assert; | |
16 | import org.eclipse.core.runtime.IAdaptable; | |
17 | ||
18 | /** | |
19 | * A codemining provider that can provide adapters through a context that can be set by the creator | |
20 | * of this codemining provider. | |
21 | * <p> | |
22 | * Clients may subclass. | |
23 | * </p> | |
24 | * | |
25 | * @since 3.13 | |
26 | */ | |
27 | public abstract class AbstractCodeMiningProvider implements ICodeMiningProvider { | |
28 | ||
29 | /** | |
30 | * The context of this codemining provider. | |
31 | */ | |
32 | private IAdaptable context; | |
33 | ||
34 | /** | |
35 | * Sets this codemining provider's context which is responsible to provide the adapters. | |
36 | * | |
37 | * @param context the context for this codemining provider | |
38 | * @throws IllegalArgumentException if the context is <code>null</code> | |
39 | * @throws IllegalStateException if this method is called more than once | |
40 | */ | |
41 | public final void setContext(IAdaptable context) throws IllegalStateException, IllegalArgumentException { | |
42 | Assert.isLegal(context != null); | |
43 | if (this.context != null) | |
44 | throw new IllegalStateException(); | |
45 | this.context= context; | |
46 | } | |
47 | ||
48 | @Override | |
49 | public void dispose() { | |
50 | context= null; | |
51 | } | |
52 | ||
53 | /** | |
54 | * Returns an object which is an instance of the given class and provides additional context for | |
55 | * this codemining provider. | |
56 | * | |
57 | * @param adapterClass the adapter class to look up | |
58 | * @return an instance that can be cast to the given class, or <code>null</code> if this object | |
59 | * does not have an adapter for the given class | |
60 | */ | |
61 | protected final <T> T getAdapter(Class<T> adapterClass) { | |
62 | Assert.isLegal(adapterClass != null); | |
63 | if (context != null) | |
64 | return context.getAdapter(adapterClass); | |
65 | return null; | |
66 | } | |
67 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import org.eclipse.core.runtime.Assert; | |
16 | import org.eclipse.core.runtime.IAdaptable; | |
17 | ||
18 | /** | |
19 | * A codemining provider that can provide adapters through a context that can be set by the creator | |
20 | * of this codemining provider. | |
21 | * <p> | |
22 | * Clients may subclass. | |
23 | * </p> | |
24 | * | |
25 | * @since 3.13 | |
26 | */ | |
27 | public abstract class AbstractCodeMiningProvider implements ICodeMiningProvider { | |
28 | ||
29 | /** | |
30 | * The context of this codemining provider. | |
31 | */ | |
32 | private IAdaptable context; | |
33 | ||
34 | /** | |
35 | * Sets this codemining provider's context which is responsible to provide the adapters. | |
36 | * | |
37 | * @param context the context for this codemining provider | |
38 | * @throws IllegalArgumentException if the context is <code>null</code> | |
39 | * @throws IllegalStateException if this method is called more than once | |
40 | */ | |
41 | public final void setContext(IAdaptable context) throws IllegalStateException, IllegalArgumentException { | |
42 | Assert.isLegal(context != null); | |
43 | if (this.context != null) | |
44 | throw new IllegalStateException(); | |
45 | this.context= context; | |
46 | } | |
47 | ||
48 | @Override | |
49 | public void dispose() { | |
50 | context= null; | |
51 | } | |
52 | ||
53 | /** | |
54 | * Returns an object which is an instance of the given class and provides additional context for | |
55 | * this codemining provider. | |
56 | * | |
57 | * @param adapterClass the adapter class to look up | |
58 | * @return an instance that can be cast to the given class, or <code>null</code> if this object | |
59 | * does not have an adapter for the given class | |
60 | */ | |
61 | protected final <T> T getAdapter(Class<T> adapterClass) { | |
62 | Assert.isLegal(adapterClass != null); | |
63 | if (context != null) | |
64 | return context.getAdapter(adapterClass); | |
65 | return null; | |
66 | } | |
67 | } |
+44
-44
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide extension point for CodeMining - Bug 528419 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import org.eclipse.jface.text.IDocument; | |
16 | import org.eclipse.jface.text.ITextViewer; | |
17 | import org.eclipse.jface.text.reconciler.Reconciler; | |
18 | ||
19 | /** | |
20 | * A reconciler which update code minings. | |
21 | * | |
22 | * @since 3.13 | |
23 | */ | |
24 | public class CodeMiningReconciler extends Reconciler { | |
25 | ||
26 | private CodeMiningStrategy fStrategy; | |
27 | ||
28 | public CodeMiningReconciler() { | |
29 | super.setIsIncrementalReconciler(false); | |
30 | fStrategy= new CodeMiningStrategy(); | |
31 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
32 | } | |
33 | ||
34 | @Override | |
35 | public void install(ITextViewer textViewer) { | |
36 | super.install(textViewer); | |
37 | fStrategy.install(textViewer); | |
38 | } | |
39 | ||
40 | @Override | |
41 | public void uninstall() { | |
42 | super.uninstall(); | |
43 | fStrategy.uninstall(); | |
44 | } | |
45 | ||
46 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide extension point for CodeMining - Bug 528419 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import org.eclipse.jface.text.IDocument; | |
16 | import org.eclipse.jface.text.ITextViewer; | |
17 | import org.eclipse.jface.text.reconciler.Reconciler; | |
18 | ||
19 | /** | |
20 | * A reconciler which update code minings. | |
21 | * | |
22 | * @since 3.13 | |
23 | */ | |
24 | public class CodeMiningReconciler extends Reconciler { | |
25 | ||
26 | private CodeMiningStrategy fStrategy; | |
27 | ||
28 | public CodeMiningReconciler() { | |
29 | super.setIsIncrementalReconciler(false); | |
30 | fStrategy= new CodeMiningStrategy(); | |
31 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
32 | } | |
33 | ||
34 | @Override | |
35 | public void install(ITextViewer textViewer) { | |
36 | super.install(textViewer); | |
37 | fStrategy.install(textViewer); | |
38 | } | |
39 | ||
40 | @Override | |
41 | public void uninstall() { | |
42 | super.uninstall(); | |
43 | fStrategy.uninstall(); | |
44 | } | |
45 | ||
46 | } |
+72
-72
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide extension point for CodeMining - Bug 528419 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import org.eclipse.core.runtime.IProgressMonitor; | |
16 | ||
17 | import org.eclipse.jface.text.IDocument; | |
18 | import org.eclipse.jface.text.IRegion; | |
19 | import org.eclipse.jface.text.ITextViewer; | |
20 | import org.eclipse.jface.text.reconciler.DirtyRegion; | |
21 | import org.eclipse.jface.text.reconciler.IReconcilingStrategy; | |
22 | import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; | |
23 | import org.eclipse.jface.text.source.ISourceViewerExtension5; | |
24 | ||
25 | /** | |
26 | * A reconciling strategy which updates code minings. | |
27 | * | |
28 | * @since 3.13 | |
29 | */ | |
30 | class CodeMiningStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension { | |
31 | ||
32 | private ISourceViewerExtension5 fViewer; | |
33 | ||
34 | public void install(ITextViewer viewer) { | |
35 | if (viewer instanceof ISourceViewerExtension5) { | |
36 | fViewer= (ISourceViewerExtension5) viewer; | |
37 | } | |
38 | } | |
39 | ||
40 | @Override | |
41 | public void initialReconcile() { | |
42 | // Do nothing | |
43 | // Initial reconcilation will happen when the SourceViewer | |
44 | // has initialized the code mining provider | |
45 | // see SourceViewer#ensureCodeMiningManagerInstalled | |
46 | } | |
47 | ||
48 | @Override | |
49 | public void reconcile(IRegion partition) { | |
50 | if (fViewer != null) { | |
51 | fViewer.updateCodeMinings(); | |
52 | } | |
53 | } | |
54 | ||
55 | public void uninstall() { | |
56 | fViewer= null; | |
57 | } | |
58 | ||
59 | @Override | |
60 | public void setProgressMonitor(IProgressMonitor monitor) { | |
61 | // Do nothing | |
62 | } | |
63 | ||
64 | @Override | |
65 | public void setDocument(IDocument document) { | |
66 | // Do nothing | |
67 | } | |
68 | ||
69 | @Override | |
70 | public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
71 | // Do nothing | |
72 | } | |
73 | ||
74 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide extension point for CodeMining - Bug 528419 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import org.eclipse.core.runtime.IProgressMonitor; | |
16 | ||
17 | import org.eclipse.jface.text.IDocument; | |
18 | import org.eclipse.jface.text.IRegion; | |
19 | import org.eclipse.jface.text.ITextViewer; | |
20 | import org.eclipse.jface.text.reconciler.DirtyRegion; | |
21 | import org.eclipse.jface.text.reconciler.IReconcilingStrategy; | |
22 | import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; | |
23 | import org.eclipse.jface.text.source.ISourceViewerExtension5; | |
24 | ||
25 | /** | |
26 | * A reconciling strategy which updates code minings. | |
27 | * | |
28 | * @since 3.13 | |
29 | */ | |
30 | class CodeMiningStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension { | |
31 | ||
32 | private ISourceViewerExtension5 fViewer; | |
33 | ||
34 | public void install(ITextViewer viewer) { | |
35 | if (viewer instanceof ISourceViewerExtension5) { | |
36 | fViewer= (ISourceViewerExtension5) viewer; | |
37 | } | |
38 | } | |
39 | ||
40 | @Override | |
41 | public void initialReconcile() { | |
42 | // Do nothing | |
43 | // Initial reconcilation will happen when the SourceViewer | |
44 | // has initialized the code mining provider | |
45 | // see SourceViewer#ensureCodeMiningManagerInstalled | |
46 | } | |
47 | ||
48 | @Override | |
49 | public void reconcile(IRegion partition) { | |
50 | if (fViewer != null) { | |
51 | fViewer.updateCodeMinings(); | |
52 | } | |
53 | } | |
54 | ||
55 | public void uninstall() { | |
56 | fViewer= null; | |
57 | } | |
58 | ||
59 | @Override | |
60 | public void setProgressMonitor(IProgressMonitor monitor) { | |
61 | // Do nothing | |
62 | } | |
63 | ||
64 | @Override | |
65 | public void setDocument(IDocument document) { | |
66 | // Do nothing | |
67 | } | |
68 | ||
69 | @Override | |
70 | public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
71 | // Do nothing | |
72 | } | |
73 | ||
74 | } |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import java.util.concurrent.CompletableFuture; | |
16 | import java.util.function.Consumer; | |
17 | ||
18 | import org.eclipse.swt.custom.StyledText; | |
19 | import org.eclipse.swt.events.MouseEvent; | |
20 | import org.eclipse.swt.graphics.Color; | |
21 | import org.eclipse.swt.graphics.GC; | |
22 | import org.eclipse.swt.graphics.Point; | |
23 | ||
24 | import org.eclipse.core.runtime.IProgressMonitor; | |
25 | ||
26 | import org.eclipse.jface.text.ITextViewer; | |
27 | import org.eclipse.jface.text.Position; | |
28 | ||
29 | /** | |
30 | * A code mining represents a content (ex: label, icons) that should be shown along with source | |
31 | * text, like the number of references, a way to run tests (with run/debug icons), etc. | |
32 | * | |
33 | * A code mining is unresolved when no content (ex: label, icons) is associated to it. For | |
34 | * performance reasons the creation of a code mining and resolving should be done to two stages. | |
35 | * | |
36 | * @since 3.13 | |
37 | */ | |
38 | public interface ICodeMining { | |
39 | ||
40 | /** | |
41 | * Returns the line position where code mining must be displayed in the line spacing area. | |
42 | * | |
43 | * @return the line position where code mining must be displayed in the line spacing area. | |
44 | */ | |
45 | Position getPosition(); | |
46 | ||
47 | /** | |
48 | * Returns the owner provider which has created this mining. | |
49 | * | |
50 | * @return the owner provider which has created this mining. | |
51 | */ | |
52 | ICodeMiningProvider getProvider(); | |
53 | ||
54 | /** | |
55 | * Returns the label may be set early in the class lifecycle, or upon completion of the future | |
56 | * provided by {@link #resolve(ITextViewer, IProgressMonitor)} operation. | |
57 | * | |
58 | * <p> | |
59 | * The returned label can have several values: | |
60 | * <ul> | |
61 | * <li><code>null</code> when mining is not resolved</li> | |
62 | * <li><code>null</code> when mining is resolved means that mining was resolved with an error and it will not | |
63 | * be displayed.</li> | |
64 | * <li>empty when mining is resolved means that mining will not be displayed</li> | |
65 | * <li>non empty when mining must be displayed</li> | |
66 | * </ul> | |
67 | * </p> | |
68 | * | |
69 | * @return the label may be set early in the class lifecycle, or upon completion of the future | |
70 | * provided by {@link #resolve(ITextViewer, IProgressMonitor)} operation. | |
71 | */ | |
72 | String getLabel(); | |
73 | ||
74 | /** | |
75 | * Returns the future to resolve the content of mining, or | |
76 | * {@link CompletableFuture#completedFuture(Object)} if no such resolution is necessary (in | |
77 | * which case {#isResolved()} is expected to return <code>true</code>). | |
78 | * | |
79 | * @param viewer the viewer. | |
80 | * @param monitor the monitor. | |
81 | * @return the future to resolve the content of mining, or | |
82 | * {@link CompletableFuture#completedFuture(Object)} if no such resolution is necessary | |
83 | * (in which case {#isResolved()} is expected to return <code>true</code>). | |
84 | */ | |
85 | CompletableFuture<Void> resolve(ITextViewer viewer, IProgressMonitor monitor); | |
86 | ||
87 | /** | |
88 | * Returns whether the content mining is resolved. If it is not resolved, | |
89 | * {{@link #resolve(ITextViewer, IProgressMonitor)}} will be invoked later, triggering the | |
90 | * future to resolve content. | |
91 | * | |
92 | * @return whether the content mining is resolved. If it is not resolved, | |
93 | * {{@link #resolve(ITextViewer, IProgressMonitor)}} will be invoked later, triggering | |
94 | * the future to resolve content. | |
95 | */ | |
96 | boolean isResolved(); | |
97 | ||
98 | /** | |
99 | * Draw the code mining. | |
100 | * | |
101 | * @param gc the graphics context | |
102 | * @param textWidget the text widget to draw on | |
103 | * @param color the color of the line | |
104 | * @param x the x position of the annotation | |
105 | * @param y the y position of the annotation | |
106 | * @return the size of the draw of mining. | |
107 | */ | |
108 | Point draw(GC gc, StyledText textWidget, Color color, int x, int y); | |
109 | ||
110 | /** | |
111 | * Returns the action to execute when mining is clicked and null otherwise. | |
112 | * | |
113 | * @return the action to execute when mining is clicked and null otherwise. | |
114 | */ | |
115 | Consumer<MouseEvent> getAction(); | |
116 | ||
117 | /** | |
118 | * Dispose the mining. Typically shuts down or cancels all related asynchronous operations. | |
119 | */ | |
120 | void dispose(); | |
121 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import java.util.concurrent.CompletableFuture; | |
16 | import java.util.function.Consumer; | |
17 | ||
18 | import org.eclipse.swt.custom.StyledText; | |
19 | import org.eclipse.swt.events.MouseEvent; | |
20 | import org.eclipse.swt.graphics.Color; | |
21 | import org.eclipse.swt.graphics.GC; | |
22 | import org.eclipse.swt.graphics.Point; | |
23 | ||
24 | import org.eclipse.core.runtime.IProgressMonitor; | |
25 | ||
26 | import org.eclipse.jface.text.ITextViewer; | |
27 | import org.eclipse.jface.text.Position; | |
28 | ||
29 | /** | |
30 | * A code mining represents a content (ex: label, icons) that should be shown along with source | |
31 | * text, like the number of references, a way to run tests (with run/debug icons), etc. | |
32 | * | |
33 | * A code mining is unresolved when no content (ex: label, icons) is associated to it. For | |
34 | * performance reasons the creation of a code mining and resolving should be done to two stages. | |
35 | * | |
36 | * @since 3.13 | |
37 | */ | |
38 | public interface ICodeMining { | |
39 | ||
40 | /** | |
41 | * Returns the line position where code mining must be displayed in the line spacing area. | |
42 | * | |
43 | * @return the line position where code mining must be displayed in the line spacing area. | |
44 | */ | |
45 | Position getPosition(); | |
46 | ||
47 | /** | |
48 | * Returns the owner provider which has created this mining. | |
49 | * | |
50 | * @return the owner provider which has created this mining. | |
51 | */ | |
52 | ICodeMiningProvider getProvider(); | |
53 | ||
54 | /** | |
55 | * Returns the label may be set early in the class lifecycle, or upon completion of the future | |
56 | * provided by {@link #resolve(ITextViewer, IProgressMonitor)} operation. | |
57 | * | |
58 | * <p> | |
59 | * The returned label can have several values: | |
60 | * <ul> | |
61 | * <li><code>null</code> when mining is not resolved</li> | |
62 | * <li><code>null</code> when mining is resolved means that mining was resolved with an error and it will not | |
63 | * be displayed.</li> | |
64 | * <li>empty when mining is resolved means that mining will not be displayed</li> | |
65 | * <li>non empty when mining must be displayed</li> | |
66 | * </ul> | |
67 | * </p> | |
68 | * | |
69 | * @return the label may be set early in the class lifecycle, or upon completion of the future | |
70 | * provided by {@link #resolve(ITextViewer, IProgressMonitor)} operation. | |
71 | */ | |
72 | String getLabel(); | |
73 | ||
74 | /** | |
75 | * Returns the future to resolve the content of mining, or | |
76 | * {@link CompletableFuture#completedFuture(Object)} if no such resolution is necessary (in | |
77 | * which case {#isResolved()} is expected to return <code>true</code>). | |
78 | * | |
79 | * @param viewer the viewer. | |
80 | * @param monitor the monitor. | |
81 | * @return the future to resolve the content of mining, or | |
82 | * {@link CompletableFuture#completedFuture(Object)} if no such resolution is necessary | |
83 | * (in which case {#isResolved()} is expected to return <code>true</code>). | |
84 | */ | |
85 | CompletableFuture<Void> resolve(ITextViewer viewer, IProgressMonitor monitor); | |
86 | ||
87 | /** | |
88 | * Returns whether the content mining is resolved. If it is not resolved, | |
89 | * {{@link #resolve(ITextViewer, IProgressMonitor)}} will be invoked later, triggering the | |
90 | * future to resolve content. | |
91 | * | |
92 | * @return whether the content mining is resolved. If it is not resolved, | |
93 | * {{@link #resolve(ITextViewer, IProgressMonitor)}} will be invoked later, triggering | |
94 | * the future to resolve content. | |
95 | */ | |
96 | boolean isResolved(); | |
97 | ||
98 | /** | |
99 | * Draw the code mining. | |
100 | * | |
101 | * @param gc the graphics context | |
102 | * @param textWidget the text widget to draw on | |
103 | * @param color the color of the line | |
104 | * @param x the x position of the annotation | |
105 | * @param y the y position of the annotation | |
106 | * @return the size of the draw of mining. | |
107 | */ | |
108 | Point draw(GC gc, StyledText textWidget, Color color, int x, int y); | |
109 | ||
110 | /** | |
111 | * Returns the action to execute when mining is clicked and null otherwise. | |
112 | * | |
113 | * @return the action to execute when mining is clicked and null otherwise. | |
114 | */ | |
115 | Consumer<MouseEvent> getAction(); | |
116 | ||
117 | /** | |
118 | * Dispose the mining. Typically shuts down or cancels all related asynchronous operations. | |
119 | */ | |
120 | void dispose(); | |
121 | } |
+45
-45
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import java.util.List; | |
16 | import java.util.concurrent.CompletableFuture; | |
17 | ||
18 | import org.eclipse.core.runtime.IProgressMonitor; | |
19 | ||
20 | import org.eclipse.jface.text.ITextViewer; | |
21 | ||
22 | /** | |
23 | * A code mining provider adds minings {@link ICodeMining} to source text. The mining will be shown | |
24 | * as dedicated horizontal lines in between the source text. | |
25 | * | |
26 | * @since 3.13 | |
27 | */ | |
28 | public interface ICodeMiningProvider { | |
29 | ||
30 | /** | |
31 | * Compute a list of code minings {@link ICodeMining}. This call should return as fast as | |
32 | * possible and if computing the content of {@link ICodeMining} is expensive implementors should | |
33 | * only return code mining objects with the position and implement resolve | |
34 | * {@link ICodeMining#resolve(ITextViewer, IProgressMonitor)}. | |
35 | * | |
36 | * @param viewer the viewer in which the command was invoked. | |
37 | * @param monitor A progress monitor. | |
38 | * @return An array of future of code minings that resolves to such. The lack of a result can be | |
39 | * signaled by returning null, or an empty array. | |
40 | */ | |
41 | CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor); | |
42 | ||
43 | /** | |
44 | * Dispose code mining provider. | |
45 | */ | |
46 | void dispose(); | |
47 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide CodeMining support with CodeMiningManager - Bug 527720 | |
12 | */ | |
13 | package org.eclipse.jface.text.codemining; | |
14 | ||
15 | import java.util.List; | |
16 | import java.util.concurrent.CompletableFuture; | |
17 | ||
18 | import org.eclipse.core.runtime.IProgressMonitor; | |
19 | ||
20 | import org.eclipse.jface.text.ITextViewer; | |
21 | ||
22 | /** | |
23 | * A code mining provider adds minings {@link ICodeMining} to source text. The mining will be shown | |
24 | * as dedicated horizontal lines in between the source text. | |
25 | * | |
26 | * @since 3.13 | |
27 | */ | |
28 | public interface ICodeMiningProvider { | |
29 | ||
30 | /** | |
31 | * Compute a list of code minings {@link ICodeMining}. This call should return as fast as | |
32 | * possible and if computing the content of {@link ICodeMining} is expensive implementors should | |
33 | * only return code mining objects with the position and implement resolve | |
34 | * {@link ICodeMining#resolve(ITextViewer, IProgressMonitor)}. | |
35 | * | |
36 | * @param viewer the viewer in which the command was invoked. | |
37 | * @param monitor A progress monitor. | |
38 | * @return An array of future of code minings that resolves to such. The lack of a result can be | |
39 | * signaled by returning null, or an empty array. | |
40 | */ | |
41 | CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor); | |
42 | ||
43 | /** | |
44 | * Dispose code mining provider. | |
45 | */ | |
46 | void dispose(); | |
47 | } |
+109
-109
62 | 62 | /** |
63 | 63 | * @return the delay in milliseconds before this task should be run |
64 | 64 | */ |
65 | public abstract long delay(); | |
66 | /** | |
67 | * Runs this task. | |
68 | */ | |
69 | @Override | |
65 | public abstract long delay(); | |
66 | /** | |
67 | * Runs this task. | |
68 | */ | |
69 | @Override | |
70 | 70 | public abstract void run(); |
71 | /** | |
72 | * @return the task to be scheduled after this task has been run | |
73 | */ | |
74 | public abstract Task nextTask(); | |
71 | /** | |
72 | * @return the task to be scheduled after this task has been run | |
73 | */ | |
74 | public abstract Task nextTask(); | |
75 | 75 | } |
76 | 76 | |
77 | 77 | /** |
87 | 87 | @Override |
88 | 88 | public Task nextTask() { |
89 | 89 | Assert.isTrue(false); |
90 | return null; | |
90 | return null; | |
91 | 91 | } |
92 | 92 | |
93 | 93 | @Override |
111 | 111 | @Override |
112 | 112 | protected IStatus run(IProgressMonitor monitor) { |
113 | 113 | Object info; |
114 | try { | |
115 | info= proposal.getAdditionalProposalInfo(monitor); | |
116 | } catch (RuntimeException x) { | |
117 | /* | |
114 | try { | |
115 | info= proposal.getAdditionalProposalInfo(monitor); | |
116 | } catch (RuntimeException x) { | |
117 | /* | |
118 | 118 | * XXX: This is the safest fix at this point so close to end of 3.2. |
119 | 119 | * Will be revisited when fixing https://bugs.eclipse.org/bugs/show_bug.cgi?id=101033 |
120 | */ | |
121 | return new Status(IStatus.WARNING, "org.eclipse.jface.text", IStatus.OK, "", x); //$NON-NLS-1$ //$NON-NLS-2$ | |
122 | } | |
120 | */ | |
121 | return new Status(IStatus.WARNING, "org.eclipse.jface.text", IStatus.OK, "", x); //$NON-NLS-1$ //$NON-NLS-2$ | |
122 | } | |
123 | 123 | setInfo((ICompletionProposal) proposal, info); |
124 | 124 | return Status.OK_STATUS; |
125 | 125 | } |
199 | 199 | |
200 | 200 | @Override |
201 | 201 | public String toString() { |
202 | return "LEGACY_WAIT"; //$NON-NLS-1$ | |
202 | return "LEGACY_WAIT"; //$NON-NLS-1$ | |
203 | 203 | } |
204 | 204 | }; |
205 | 205 | /** |
208 | 208 | private final Task EXIT= new Task() { |
209 | 209 | @Override |
210 | 210 | public long delay() { |
211 | return 1; | |
212 | } | |
211 | return 1; | |
212 | } | |
213 | 213 | |
214 | 214 | @Override |
215 | 215 | public Task nextTask() { |
216 | 216 | Assert.isTrue(false); |
217 | return EXIT; | |
218 | } | |
217 | return EXIT; | |
218 | } | |
219 | 219 | |
220 | 220 | @Override |
221 | 221 | public void run() { |
222 | 222 | Assert.isTrue(false); |
223 | } | |
223 | } | |
224 | 224 | |
225 | 225 | @Override |
226 | 226 | public String toString() { |
250 | 250 | * @param delay the delay until to show additional info |
251 | 251 | */ |
252 | 252 | public Timer(Display display, int delay) { |
253 | fDisplay= display; | |
253 | fDisplay= display; | |
254 | 254 | fDelay= delay; |
255 | 255 | long current= System.currentTimeMillis(); |
256 | schedule(IDLE, current); | |
256 | schedule(IDLE, current); | |
257 | 257 | |
258 | 258 | fThread= new Thread((Runnable) () -> { |
259 | 259 | try { |
291 | 291 | } |
292 | 292 | } |
293 | 293 | |
294 | private Task taskOnReset(ICompletionProposal p) { | |
294 | private Task taskOnReset(ICompletionProposal p) { | |
295 | 295 | if (p == null) |
296 | 296 | return IDLE; |
297 | 297 | if (isExt5(p)) |
298 | 298 | return FIRST_WAIT; |
299 | 299 | return LEGACY_WAIT; |
300 | } | |
301 | ||
302 | private synchronized void loop() throws InterruptedException { | |
303 | long current= System.currentTimeMillis(); | |
304 | Task task= currentTask(); | |
305 | ||
306 | while (task != EXIT) { | |
307 | long delay= fNextWakeup - current; | |
308 | if (delay <= 0) { | |
309 | task.run(); | |
310 | task= task.nextTask(); | |
311 | schedule(task, current); | |
312 | } else { | |
313 | wait(delay); | |
314 | current= System.currentTimeMillis(); | |
315 | task= currentTask(); | |
316 | } | |
317 | } | |
318 | } | |
319 | ||
320 | private Task currentTask() { | |
321 | return fTask; | |
322 | } | |
323 | ||
324 | private void schedule(Task task, long current) { | |
325 | fTask= task; | |
326 | long nextWakeup= current + task.delay(); | |
327 | if (nextWakeup <= current) | |
328 | fNextWakeup= Long.MAX_VALUE; | |
329 | else | |
330 | fNextWakeup= nextWakeup; | |
331 | } | |
300 | } | |
301 | ||
302 | private synchronized void loop() throws InterruptedException { | |
303 | long current= System.currentTimeMillis(); | |
304 | Task task= currentTask(); | |
305 | ||
306 | while (task != EXIT) { | |
307 | long delay= fNextWakeup - current; | |
308 | if (delay <= 0) { | |
309 | task.run(); | |
310 | task= task.nextTask(); | |
311 | schedule(task, current); | |
312 | } else { | |
313 | wait(delay); | |
314 | current= System.currentTimeMillis(); | |
315 | task= currentTask(); | |
316 | } | |
317 | } | |
318 | } | |
319 | ||
320 | private Task currentTask() { | |
321 | return fTask; | |
322 | } | |
323 | ||
324 | private void schedule(Task task, long current) { | |
325 | fTask= task; | |
326 | long nextWakeup= current + task.delay(); | |
327 | if (nextWakeup <= current) | |
328 | fNextWakeup= Long.MAX_VALUE; | |
329 | else | |
330 | fNextWakeup= nextWakeup; | |
331 | } | |
332 | 332 | |
333 | 333 | private boolean isExt5(ICompletionProposal p) { |
334 | 334 | return p instanceof ICompletionProposalExtension5; |
335 | 335 | } |
336 | 336 | |
337 | ICompletionProposal getCurrentProposal() { | |
338 | return fCurrentProposal; | |
339 | } | |
340 | ||
341 | ICompletionProposalExtension5 getCurrentProposalEx() { | |
342 | Assert.isTrue(fCurrentProposal instanceof ICompletionProposalExtension5); | |
343 | return (ICompletionProposalExtension5) fCurrentProposal; | |
344 | } | |
345 | ||
346 | synchronized void setInfo(ICompletionProposal proposal, Object info) { | |
347 | if (proposal == fCurrentProposal) { | |
348 | fCurrentInfo= info; | |
349 | if (fAllowShowing) { | |
350 | triggerShowing(); | |
351 | } | |
352 | } | |
353 | } | |
354 | ||
355 | private void triggerShowing() { | |
337 | ICompletionProposal getCurrentProposal() { | |
338 | return fCurrentProposal; | |
339 | } | |
340 | ||
341 | ICompletionProposalExtension5 getCurrentProposalEx() { | |
342 | Assert.isTrue(fCurrentProposal instanceof ICompletionProposalExtension5); | |
343 | return (ICompletionProposalExtension5) fCurrentProposal; | |
344 | } | |
345 | ||
346 | synchronized void setInfo(ICompletionProposal proposal, Object info) { | |
347 | if (proposal == fCurrentProposal) { | |
348 | fCurrentInfo= info; | |
349 | if (fAllowShowing) { | |
350 | triggerShowing(); | |
351 | } | |
352 | } | |
353 | } | |
354 | ||
355 | private void triggerShowing() { | |
356 | 356 | final Object info= fCurrentInfo; |
357 | 357 | if (!fDisplay.isDisposed()) { |
358 | 358 | fDisplay.asyncExec(() -> { |
363 | 363 | } |
364 | 364 | }); |
365 | 365 | } |
366 | } | |
367 | ||
368 | /** | |
369 | * Called in the display thread to show additional info. | |
370 | * | |
371 | * @param proposal the proposal to show information about | |
372 | * @param info the information about <code>proposal</code> | |
373 | */ | |
374 | protected abstract void showInformation(ICompletionProposal proposal, Object info); | |
375 | ||
376 | void allowShowing() { | |
377 | fAllowShowing= true; | |
378 | triggerShowing(); | |
379 | } | |
366 | } | |
367 | ||
368 | /** | |
369 | * Called in the display thread to show additional info. | |
370 | * | |
371 | * @param proposal the proposal to show information about | |
372 | * @param info the information about <code>proposal</code> | |
373 | */ | |
374 | protected abstract void showInformation(ICompletionProposal proposal, Object info); | |
375 | ||
376 | void allowShowing() { | |
377 | fAllowShowing= true; | |
378 | triggerShowing(); | |
379 | } | |
380 | 380 | } |
381 | 381 | /** |
382 | 382 | * Internal table selection listener. |
440 | 440 | setAnchor(ANCHOR_RIGHT); |
441 | 441 | setFallbackAnchors(new Anchor[] { ANCHOR_RIGHT, ANCHOR_LEFT, ANCHOR_BOTTOM }); |
442 | 442 | |
443 | /* | |
443 | /* | |
444 | 444 | * Adjust the location by one pixel towards the proposal popup, so that the single pixel |
445 | 445 | * border of the additional info popup overlays with the border of the popup. This avoids |
446 | 446 | * having a double black line. |
447 | 447 | */ |
448 | int spacing= -1; | |
448 | int spacing= -1; | |
449 | 449 | setMargins(spacing, spacing); // see also adjustment in #computeLocation |
450 | 450 | |
451 | 451 | InformationControlReplacer replacer= new InformationControlReplacer(new DefaultPresenterControlCreator()); |
524 | 524 | if (fProposal == proposal && ((info == null && fInformation == null) || (info != null && info.equals(fInformation)))) |
525 | 525 | return; |
526 | 526 | |
527 | fInformation= info; | |
528 | fProposal= proposal; | |
529 | showInformation(); | |
530 | } | |
527 | fInformation= info; | |
528 | fProposal= proposal; | |
529 | showInformation(); | |
530 | } | |
531 | 531 | |
532 | 532 | @Override |
533 | 533 | protected void computeInformation() { |
545 | 545 | |
546 | 546 | @Override |
547 | 547 | protected Point computeLocation(Rectangle subjectArea, Point controlSize, Anchor anchor) { |
548 | Point location= super.computeLocation(subjectArea, controlSize, anchor); | |
549 | ||
550 | /* | |
548 | Point location= super.computeLocation(subjectArea, controlSize, anchor); | |
549 | ||
550 | /* | |
551 | 551 | * The location is computed using subjectControl.toDisplay(), which does not include the |
552 | 552 | * trim of the subject control. As we want the additional info popup aligned with the outer |
553 | 553 | * coordinates of the proposal popup, adjust this here |
554 | 554 | */ |
555 | Rectangle trim= fProposalTable.getShell().computeTrim(0, 0, 0, 0); | |
556 | location.x += trim.x; | |
557 | location.y += trim.y; | |
555 | Rectangle trim= fProposalTable.getShell().computeTrim(0, 0, 0, 0); | |
556 | location.x += trim.x; | |
557 | location.y += trim.y; | |
558 | 558 | |
559 | 559 | return location; |
560 | 560 | } |
565 | 565 | Point sizeConstraint= super.computeSizeConstraints(subjectControl, informationControl); |
566 | 566 | Point size= subjectControl.getShell().getSize(); |
567 | 567 | |
568 | // AbstractInformationControlManager#internalShowInformationControl(Rectangle, Object) adds trims | |
568 | // AbstractInformationControlManager#internalShowInformationControl(Rectangle, Object) adds trims | |
569 | 569 | // to the computed constraints. Need to remove them here, to make the outer bounds of the additional |
570 | 570 | // info shell fit the bounds of the proposal shell: |
571 | if (fInformationControl instanceof IInformationControlExtension3) { | |
572 | Rectangle shellTrim= ((IInformationControlExtension3) fInformationControl).computeTrim(); | |
573 | size.x -= shellTrim.width; | |
574 | size.y -= shellTrim.height; | |
575 | } | |
571 | if (fInformationControl instanceof IInformationControlExtension3) { | |
572 | Rectangle shellTrim= ((IInformationControlExtension3) fInformationControl).computeTrim(); | |
573 | size.x -= shellTrim.width; | |
574 | size.y -= shellTrim.height; | |
575 | } | |
576 | 576 | |
577 | 577 | if (sizeConstraint.x < size.x) |
578 | 578 | sizeConstraint.x= size.x; |
+40
-40
739 | 739 | * @param control the control to watch for focus |
740 | 740 | * @since 3.2 |
741 | 741 | */ |
742 | private void addCommandSupport(final Control control) { | |
743 | final KeySequence commandSequence= fContentAssistant.getRepeatedInvocationKeySequence(); | |
744 | if (commandSequence != null && !commandSequence.isEmpty() && fContentAssistant.isRepeatedInvocationMode()) { | |
745 | control.addFocusListener(new FocusListener() { | |
746 | private CommandKeyListener fCommandKeyListener; | |
747 | @Override | |
742 | private void addCommandSupport(final Control control) { | |
743 | final KeySequence commandSequence= fContentAssistant.getRepeatedInvocationKeySequence(); | |
744 | if (commandSequence != null && !commandSequence.isEmpty() && fContentAssistant.isRepeatedInvocationMode()) { | |
745 | control.addFocusListener(new FocusListener() { | |
746 | private CommandKeyListener fCommandKeyListener; | |
747 | @Override | |
748 | 748 | public void focusGained(FocusEvent e) { |
749 | if (Helper.okToUse(control)) { | |
750 | if (fCommandKeyListener == null) { | |
751 | fCommandKeyListener= new CommandKeyListener(commandSequence); | |
752 | fProposalTable.addKeyListener(fCommandKeyListener); | |
753 | } | |
754 | } | |
755 | } | |
756 | @Override | |
749 | if (Helper.okToUse(control)) { | |
750 | if (fCommandKeyListener == null) { | |
751 | fCommandKeyListener= new CommandKeyListener(commandSequence); | |
752 | fProposalTable.addKeyListener(fCommandKeyListener); | |
753 | } | |
754 | } | |
755 | } | |
756 | @Override | |
757 | 757 | public void focusLost(FocusEvent e) { |
758 | if (fCommandKeyListener != null) { | |
759 | control.removeKeyListener(fCommandKeyListener); | |
760 | fCommandKeyListener= null; | |
761 | } | |
762 | } | |
763 | }); | |
764 | } | |
765 | if (fAdditionalInfoController != null) { | |
766 | control.addFocusListener(new FocusListener() { | |
767 | private TraverseListener fTraverseListener; | |
768 | @Override | |
758 | if (fCommandKeyListener != null) { | |
759 | control.removeKeyListener(fCommandKeyListener); | |
760 | fCommandKeyListener= null; | |
761 | } | |
762 | } | |
763 | }); | |
764 | } | |
765 | if (fAdditionalInfoController != null) { | |
766 | control.addFocusListener(new FocusListener() { | |
767 | private TraverseListener fTraverseListener; | |
768 | @Override | |
769 | 769 | public void focusGained(FocusEvent e) { |
770 | if (Helper.okToUse(control)) { | |
771 | if (fTraverseListener == null) { | |
770 | if (Helper.okToUse(control)) { | |
771 | if (fTraverseListener == null) { | |
772 | 772 | fTraverseListener= new TraverseListener() { |
773 | 773 | @Override |
774 | 774 | public void keyTraversed(TraverseEvent event) { |
781 | 781 | } |
782 | 782 | } |
783 | 783 | }; |
784 | fProposalTable.addTraverseListener(fTraverseListener); | |
785 | } | |
786 | } | |
787 | } | |
788 | @Override | |
784 | fProposalTable.addTraverseListener(fTraverseListener); | |
785 | } | |
786 | } | |
787 | } | |
788 | @Override | |
789 | 789 | public void focusLost(FocusEvent e) { |
790 | if (fTraverseListener != null) { | |
791 | control.removeTraverseListener(fTraverseListener); | |
792 | fTraverseListener= null; | |
793 | } | |
794 | } | |
795 | }); | |
796 | } | |
797 | } | |
790 | if (fTraverseListener != null) { | |
791 | control.removeTraverseListener(fTraverseListener); | |
792 | fTraverseListener= null; | |
793 | } | |
794 | } | |
795 | }); | |
796 | } | |
797 | } | |
798 | 798 | |
799 | 799 | /** |
800 | 800 | * Returns the background color to use. |
+7
-7
2463 | 2463 | /** |
2464 | 2464 | * Returns the prefix completion state. |
2465 | 2465 | * |
2466 | * @return <code>true</code> if prefix completion is enabled, <code>false</code> otherwise | |
2467 | * @since 3.2 | |
2468 | */ | |
2469 | boolean isPrefixCompletionEnabled() { | |
2470 | return fIsPrefixCompletionEnabled; | |
2471 | } | |
2466 | * @return <code>true</code> if prefix completion is enabled, <code>false</code> otherwise | |
2467 | * @since 3.2 | |
2468 | */ | |
2469 | boolean isPrefixCompletionEnabled() { | |
2470 | return fIsPrefixCompletionEnabled; | |
2471 | } | |
2472 | 2472 | |
2473 | 2473 | /** |
2474 | 2474 | * Returns whether the content assistant proposal popup has the focus. |
2731 | 2731 | * @since 3.4 |
2732 | 2732 | */ |
2733 | 2733 | boolean isColoredLabelsSupportEnabled() { |
2734 | return fIsColoredLabelsSupportEnabled; | |
2734 | return fIsColoredLabelsSupportEnabled; | |
2735 | 2735 | } |
2736 | 2736 | |
2737 | 2737 | /** |
+1
-1
822 | 822 | |
823 | 823 | } else if (key == SWT.ESC) { |
824 | 824 | e.doit= false; |
825 | hideContextInfoPopup(); | |
825 | hideContextInfoPopup(); | |
826 | 826 | } else { |
827 | 827 | validateContextInformation(); |
828 | 828 | } |
+14
-14
149 | 149 | } |
150 | 150 | } |
151 | 151 | |
152 | reconcilerReset(); | |
152 | reconcilerReset(); | |
153 | 153 | } |
154 | 154 | |
155 | 155 | /** |
481 | 481 | } |
482 | 482 | fListener= null; |
483 | 483 | |
484 | synchronized (this) { | |
485 | // http://dev.eclipse.org/bugs/show_bug.cgi?id=19135 | |
486 | BackgroundThread bt= fThread; | |
487 | fThread= null; | |
488 | bt.cancel(); | |
489 | } | |
484 | synchronized (this) { | |
485 | // http://dev.eclipse.org/bugs/show_bug.cgi?id=19135 | |
486 | BackgroundThread bt= fThread; | |
487 | fThread= null; | |
488 | bt.cancel(); | |
489 | } | |
490 | 490 | } |
491 | 491 | } |
492 | 492 | |
577 | 577 | } |
578 | 578 | } |
579 | 579 | |
580 | /** | |
581 | * Hook that is called after the reconciler thread has been reset. | |
582 | */ | |
583 | protected void reconcilerReset() { | |
584 | } | |
585 | ||
586 | /** | |
580 | /** | |
581 | * Hook that is called after the reconciler thread has been reset. | |
582 | */ | |
583 | protected void reconcilerReset() { | |
584 | } | |
585 | ||
586 | /** | |
587 | 587 | * Tells whether the code is running in this reconciler's |
588 | 588 | * background thread. |
589 | 589 | * |
513 | 513 | return null; |
514 | 514 | } |
515 | 515 | |
516 | /* zero-length partition support */ | |
516 | /* zero-length partition support */ | |
517 | 517 | |
518 | 518 | @Override |
519 | 519 | public String getContentType(int offset, boolean preferOpenPartitions) { |
533 | 533 | return new TypedRegion(offset, 0, IDocument.DEFAULT_CONTENT_TYPE); |
534 | 534 | } |
535 | 535 | } |
536 | return region; | |
536 | return region; | |
537 | 537 | } |
538 | 538 | |
539 | 539 | @Override |
573 | 573 | return null; |
574 | 574 | } |
575 | 575 | |
576 | /* zero-length partition support */ | |
576 | /* zero-length partition support */ | |
577 | 577 | |
578 | 578 | /** |
579 | 579 | * {@inheritDoc} |
605 | 605 | return new TypedRegion(offset, 0, IDocument.DEFAULT_CONTENT_TYPE); |
606 | 606 | } |
607 | 607 | } |
608 | return region; | |
608 | return region; | |
609 | 609 | } |
610 | 610 | |
611 | 611 | /** |
505 | 505 | return null; |
506 | 506 | } |
507 | 507 | |
508 | /* zero-length partition support */ | |
508 | /* zero-length partition support */ | |
509 | 509 | |
510 | 510 | @Override |
511 | 511 | public String getContentType(int offset, boolean preferOpenPartitions) { |
525 | 525 | return new TypedRegion(offset, 0, IDocument.DEFAULT_CONTENT_TYPE); |
526 | 526 | } |
527 | 527 | } |
528 | return region; | |
528 | return region; | |
529 | 529 | } |
530 | 530 | |
531 | 531 | @Override |
194 | 194 | |
195 | 195 | @Override |
196 | 196 | public void unread() { |
197 | --fOffset; | |
197 | --fOffset; | |
198 | 198 | fColumn= UNDEFINED; |
199 | 199 | } |
200 | 200 | } |
+9
-9
349 | 349 | |
350 | 350 | @Override |
351 | 351 | protected void showInformationControl(Rectangle subjectArea) { |
352 | super.showInformationControl(subjectArea); | |
353 | fCurrentHover= getHover(getHoverEvent()); | |
352 | super.showInformationControl(subjectArea); | |
353 | fCurrentHover= getHover(getHoverEvent()); | |
354 | 354 | } |
355 | 355 | |
356 | 356 | @Override |
357 | 357 | protected void hideInformationControl() { |
358 | 358 | fCurrentHover= null; |
359 | super.hideInformationControl(); | |
359 | super.hideInformationControl(); | |
360 | 360 | } |
361 | 361 | |
362 | 362 | /** |
706 | 706 | * @return the currently shown annotation hover or <code>null</code> |
707 | 707 | * @since 3.2 |
708 | 708 | */ |
709 | public IAnnotationHover getCurrentAnnotationHover() { | |
710 | return fCurrentHover; | |
711 | } | |
709 | public IAnnotationHover getCurrentAnnotationHover() { | |
710 | return fCurrentHover; | |
711 | } | |
712 | 712 | |
713 | 713 | /** |
714 | 714 | * Returns an adapter that gives access to internal methods. |
721 | 721 | * @noreference This method is not intended to be referenced by clients. |
722 | 722 | * @nooverride This method is not intended to be re-implemented or extended by clients. |
723 | 723 | */ |
724 | @Override | |
724 | @Override | |
725 | 725 | public InternalAccessor getInternalAccessor() { |
726 | return new InternalAccessor() { | |
726 | return new InternalAccessor() { | |
727 | 727 | @Override |
728 | 728 | public IInformationControl getCurrentInformationControl() { |
729 | 729 | return AnnotationBarHoverManager.super.getInternalAccessor().getCurrentInformationControl(); |
769 | 769 | return fAllowMouseExit; |
770 | 770 | } |
771 | 771 | }; |
772 | } | |
772 | } | |
773 | 773 | } |
774 | 774 |
956 | 956 | private void invalidateTextPresentation() { |
957 | 957 | IRegion r= null; |
958 | 958 | synchronized (fHighlightedDecorationsMapLock) { |
959 | if (fCurrentHighlightAnnotationRange != null) | |
960 | r= new Region(fCurrentHighlightAnnotationRange.getOffset(), fCurrentHighlightAnnotationRange.getLength()); | |
959 | if (fCurrentHighlightAnnotationRange != null) | |
960 | r= new Region(fCurrentHighlightAnnotationRange.getOffset(), fCurrentHighlightAnnotationRange.getLength()); | |
961 | 961 | } |
962 | 962 | if (r == null) |
963 | 963 | return; |
1609 | 1609 | } |
1610 | 1610 | |
1611 | 1611 | /** |
1612 | * Retrieves the annotation model from the given source viewer. | |
1613 | * | |
1614 | * @param sourceViewer the source viewer | |
1615 | * @return the source viewer's annotation model or <code>null</code> if none can be found | |
1612 | * Retrieves the annotation model from the given source viewer. | |
1613 | * | |
1614 | * @param sourceViewer the source viewer | |
1615 | * @return the source viewer's annotation model or <code>null</code> if none can be found | |
1616 | 1616 | * @since 3.0 |
1617 | 1617 | */ |
1618 | 1618 | protected IAnnotationModel findAnnotationModel(ISourceViewer sourceViewer) { |
493 | 493 | } |
494 | 494 | |
495 | 495 | /** |
496 | * Returns the revision selection provider. | |
497 | * | |
498 | * @return the revision selection provider | |
499 | * @since 3.2 | |
500 | */ | |
501 | public ISelectionProvider getRevisionSelectionProvider() { | |
502 | return fRevisionPainter.getRevisionSelectionProvider(); | |
503 | } | |
496 | * Returns the revision selection provider. | |
497 | * | |
498 | * @return the revision selection provider | |
499 | * @since 3.2 | |
500 | */ | |
501 | public ISelectionProvider getRevisionSelectionProvider() { | |
502 | return fRevisionPainter.getRevisionSelectionProvider(); | |
503 | } | |
504 | 504 | } |
123 | 123 | * top of the text widget and the top of this overview ruler. |
124 | 124 | * |
125 | 125 | * @return the header control of this overview ruler. |
126 | */ | |
126 | */ | |
127 | 127 | Control getHeaderControl(); |
128 | 128 | } |
+20
-20
228 | 228 | postRedraw(); |
229 | 229 | } |
230 | 230 | |
231 | @Override | |
231 | @Override | |
232 | 232 | public ISelectionProvider getRevisionSelectionProvider() { |
233 | return fRevisionPainter.getRevisionSelectionProvider(); | |
234 | } | |
235 | ||
236 | /* | |
237 | * @see org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension#setRenderingMode(org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension.RenderingMode) | |
238 | * @since 3.3 | |
239 | */ | |
240 | @Override | |
233 | return fRevisionPainter.getRevisionSelectionProvider(); | |
234 | } | |
235 | ||
236 | /* | |
237 | * @see org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension#setRenderingMode(org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension.RenderingMode) | |
238 | * @since 3.3 | |
239 | */ | |
240 | @Override | |
241 | 241 | public void setRevisionRenderingMode(RenderingMode renderingMode) { |
242 | 242 | fRevisionPainter.setRenderingMode(renderingMode); |
243 | 243 | } |
249 | 249 | * diff / revision info. |
250 | 250 | * @since 3.3 |
251 | 251 | */ |
252 | public void showLineNumbers(boolean showNumbers) { | |
253 | if (fShowNumbers != showNumbers) { | |
254 | fShowNumbers= showNumbers; | |
252 | public void showLineNumbers(boolean showNumbers) { | |
253 | if (fShowNumbers != showNumbers) { | |
254 | fShowNumbers= showNumbers; | |
255 | 255 | updateNumberOfDigits(); |
256 | 256 | computeIndentations(); |
257 | 257 | layout(true); |
258 | } | |
259 | } | |
260 | ||
261 | @Override | |
258 | } | |
259 | } | |
260 | ||
261 | @Override | |
262 | 262 | public int getWidth() { |
263 | int width= super.getWidth(); | |
263 | int width= super.getWidth(); | |
264 | 264 | return width > 0 ? width : 8; // minimal width to display quick diff / revisions if no textual info is shown |
265 | } | |
266 | ||
267 | /** | |
265 | } | |
266 | ||
267 | /** | |
268 | 268 | * Returns <code>true</code> if the ruler is showing line numbers, <code>false</code> |
269 | 269 | * otherwise |
270 | 270 | * |
611 | 611 | VisibleLinesTracker.track(fCachedTextViewer, lineHeightChangeHandler); |
612 | 612 | |
613 | 613 | fCanvas= new Canvas(parentControl, SWT.NO_FOCUS ) { |
614 | @Override | |
614 | @Override | |
615 | 615 | public void addMouseListener(MouseListener listener) { |
616 | 616 | // see bug 40889, bug 230073 and AnnotationRulerColumn#isPropagatingMouseListener() |
617 | 617 | if (listener == fMouseHandler) |
363 | 363 | thumbHeight= verticalBar != null ? Math.max(Math.min(bounds.height, verticalBar.getThumbBounds().height), 0) : 0; |
364 | 364 | } |
365 | 365 | |
366 | int partialTopIndex= JFaceTextUtil.getPartialTopIndex(textWidget); | |
367 | int topLineHeight= textWidget.getLineHeight(textWidget.getOffsetAtLine(partialTopIndex)); | |
368 | int topLinePixel= textWidget.getLinePixel(partialTopIndex); | |
369 | double topIndex= partialTopIndex - (double) topLinePixel / topLineHeight; | |
370 | ||
371 | int partialBottomIndex= JFaceTextUtil.getPartialBottomIndex(textWidget); | |
372 | int bottomLineHeight= textWidget.getLineHeight(textWidget.getOffsetAtLine(partialBottomIndex)); | |
373 | int bottomLinePixel= textWidget.getLinePixel(partialBottomIndex); | |
374 | double bottomIndex= partialBottomIndex - ((double) bottomLinePixel - textWidget.getClientArea().height) / bottomLineHeight; | |
375 | ||
376 | visibleLines= bottomIndex - topIndex; | |
377 | invisibleLines= maxLines - visibleLines; | |
366 | int partialTopIndex= JFaceTextUtil.getPartialTopIndex(textWidget); | |
367 | int topLineHeight= textWidget.getLineHeight(textWidget.getOffsetAtLine(partialTopIndex)); | |
368 | int topLinePixel= textWidget.getLinePixel(partialTopIndex); | |
369 | double topIndex= partialTopIndex - (double) topLinePixel / topLineHeight; | |
370 | ||
371 | int partialBottomIndex= JFaceTextUtil.getPartialBottomIndex(textWidget); | |
372 | int bottomLineHeight= textWidget.getLineHeight(textWidget.getOffsetAtLine(partialBottomIndex)); | |
373 | int bottomLinePixel= textWidget.getLinePixel(partialBottomIndex); | |
374 | double bottomIndex= partialBottomIndex - ((double) bottomLinePixel - textWidget.getClientArea().height) / bottomLineHeight; | |
375 | ||
376 | visibleLines= bottomIndex - topIndex; | |
377 | invisibleLines= maxLines - visibleLines; | |
378 | 378 | } |
379 | 379 | } |
380 | 380 |
1257 | 1257 | } |
1258 | 1258 | } |
1259 | 1259 | |
1260 | @Override | |
1260 | @Override | |
1261 | 1261 | public IAnnotationHover getCurrentAnnotationHover() { |
1262 | if (fVerticalRulerHoveringController == null) | |
1263 | return null; | |
1264 | return fVerticalRulerHoveringController.getCurrentAnnotationHover(); | |
1265 | } | |
1266 | ||
1267 | @Override | |
1262 | if (fVerticalRulerHoveringController == null) | |
1263 | return null; | |
1264 | return fVerticalRulerHoveringController.getCurrentAnnotationHover(); | |
1265 | } | |
1266 | ||
1267 | @Override | |
1268 | 1268 | public void setCodeMiningProviders(ICodeMiningProvider[] codeMiningProviders) { |
1269 | 1269 | boolean enable= codeMiningProviders != null && codeMiningProviders.length > 0; |
1270 | 1270 | fCodeMiningProviders= codeMiningProviders; |
+33
-11
21 | 21 | import org.eclipse.swt.graphics.Font; |
22 | 22 | import org.eclipse.swt.graphics.GC; |
23 | 23 | |
24 | import org.eclipse.jface.text.IRegion; | |
24 | 25 | import org.eclipse.jface.text.ITextViewerExtension5; |
25 | 26 | import org.eclipse.jface.text.Position; |
27 | import org.eclipse.jface.text.Region; | |
26 | 28 | import org.eclipse.jface.text.source.Annotation; |
27 | 29 | import org.eclipse.jface.text.source.ISourceViewer; |
30 | import org.eclipse.jface.text.source.projection.ProjectionViewer; | |
28 | 31 | |
29 | 32 | /** |
30 | 33 | * Abstract class for inlined annotation. |
71 | 74 | } |
72 | 75 | |
73 | 76 | /** |
74 | * Returns the position where the annotation must be drawn. | |
75 | * | |
76 | * @return the position where the annotation must be drawn. | |
77 | * Returns the position where the annotation must be drawn. For {@link ITextViewerExtension5} | |
78 | * (enabling folding with widget/model projection), this position is the <strong>model</strong> | |
79 | * position. | |
80 | * | |
81 | * @return the model position where the annotation must be drawn. | |
77 | 82 | */ |
78 | 83 | public Position getPosition() { |
84 | return position; | |
85 | } | |
86 | ||
87 | final Position computeWidgetPosition() { | |
88 | if (fViewer instanceof ITextViewerExtension5) { | |
89 | IRegion region= ((ITextViewerExtension5) fViewer).modelRange2WidgetRange(new Region(position.getOffset(), position.getLength())); | |
90 | return new Position(region.getOffset(), region.getLength()); | |
91 | } | |
79 | 92 | return position; |
80 | 93 | } |
81 | 94 | |
118 | 131 | * Draw the inlined annotation. By default it draw the text of the annotation with gray color. |
119 | 132 | * User can override this method to draw anything. |
120 | 133 | * |
121 | * @param gc the graphics context | |
134 | * @param gc the graphics context | |
122 | 135 | * @param textWidget the text widget to draw on |
123 | * @param offset the offset of the line | |
124 | * @param length the length of the line | |
125 | * @param color the color of the line | |
126 | * @param x the x position of the annotation | |
127 | * @param y the y position of the annotation | |
128 | */ | |
129 | public void draw(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) { | |
136 | * @param widgetOffset the offset | |
137 | * @param length the length of the line | |
138 | * @param color the color of the line | |
139 | * @param x the x position of the annotation | |
140 | * @param y the y position of the annotation | |
141 | */ | |
142 | public void draw(GC gc, StyledText textWidget, int widgetOffset, int length, Color color, int x, int y) { | |
130 | 143 | gc.setForeground(color); |
131 | 144 | gc.setBackground(textWidget.getBackground()); |
132 | 145 | gc.drawString(getText(), x, y, true); |
173 | 186 | */ |
174 | 187 | protected boolean isInVisibleLines() { |
175 | 188 | return support.isInVisibleLines(getPosition().getOffset()); |
189 | } | |
190 | ||
191 | boolean isFirstVisibleOffset(int widgetOffset) { | |
192 | if (fViewer instanceof ProjectionViewer) { | |
193 | IRegion widgetRange= ((ProjectionViewer) fViewer).modelRange2WidgetRange(new Region(position.getOffset(), position.getLength())); | |
194 | return widgetOffset == widgetRange.getOffset(); | |
195 | } else { | |
196 | return position.getOffset() == widgetOffset; | |
197 | } | |
176 | 198 | } |
177 | 199 | |
178 | 200 | /** |
+124
-73
20 | 20 | import org.eclipse.swt.graphics.Point; |
21 | 21 | import org.eclipse.swt.graphics.Rectangle; |
22 | 22 | |
23 | import org.eclipse.jface.text.Position; | |
24 | 23 | import org.eclipse.jface.text.source.Annotation; |
25 | 24 | import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy; |
26 | 25 | |
32 | 31 | class InlinedAnnotationDrawingStrategy implements IDrawingStrategy { |
33 | 32 | |
34 | 33 | @Override |
35 | public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) { | |
34 | public void draw(Annotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) { | |
36 | 35 | if (!(annotation instanceof AbstractInlinedAnnotation)) { |
37 | 36 | return; |
38 | 37 | } |
39 | if (!((AbstractInlinedAnnotation) annotation).isInVisibleLines()) { | |
40 | // The annotation is not in visible lines, don't draw it. | |
41 | return; | |
42 | } | |
43 | InlinedAnnotationDrawingStrategy.draw((AbstractInlinedAnnotation) annotation, gc, textWidget, offset, length, | |
44 | color); | |
38 | AbstractInlinedAnnotation inlinedAnnotation= (AbstractInlinedAnnotation) annotation; | |
39 | if (inlinedAnnotation.isInVisibleLines() && inlinedAnnotation.isFirstVisibleOffset(widgetOffset)) { | |
40 | draw((AbstractInlinedAnnotation) annotation, gc, textWidget, widgetOffset, length, | |
41 | color); | |
42 | } | |
45 | 43 | } |
46 | 44 | |
47 | 45 | /** |
50 | 48 | * @param annotation the annotation to be drawn |
51 | 49 | * @param gc the graphics context, <code>null</code> when in clearing mode |
52 | 50 | * @param textWidget the text widget to draw on |
53 | * @param offset the offset of the line | |
51 | * @param widgetOffset the offset of the line | |
54 | 52 | * @param length the length of the line |
55 | 53 | * @param color the color of the line |
56 | 54 | */ |
57 | public static void draw(AbstractInlinedAnnotation annotation, GC gc, StyledText textWidget, int offset, int length, | |
55 | public static void draw(AbstractInlinedAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, | |
58 | 56 | Color color) { |
59 | 57 | if (annotation instanceof LineHeaderAnnotation) { |
60 | draw((LineHeaderAnnotation) annotation, gc, textWidget, offset, length, color); | |
61 | } else { | |
62 | draw((LineContentAnnotation) annotation, gc, textWidget, offset, length, color); | |
58 | draw((LineHeaderAnnotation) annotation, gc, textWidget, widgetOffset, length, color); | |
59 | } else { | |
60 | draw((LineContentAnnotation) annotation, gc, textWidget, widgetOffset, length, color); | |
63 | 61 | } |
64 | 62 | } |
65 | 63 | |
125 | 123 | * @param annotation the annotation to be drawn |
126 | 124 | * @param gc the graphics context, <code>null</code> when in clearing mode |
127 | 125 | * @param textWidget the text widget to draw on |
128 | * @param offset the offset of the line | |
126 | * @param widgetOffset the offset of the line in the widget (not model) | |
129 | 127 | * @param length the length of the line |
130 | 128 | * @param color the color of the line |
131 | 129 | */ |
132 | private static void draw(LineContentAnnotation annotation, GC gc, StyledText textWidget, int offset, int length, | |
130 | private static void draw(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, | |
133 | 131 | Color color) { |
132 | if (annotation.drawRightToPreviousChar(widgetOffset)) { | |
133 | drawAsRightOfPreviousCharacter(annotation, gc, textWidget, widgetOffset, length, color); | |
134 | } else { | |
135 | drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color); | |
136 | } | |
137 | } | |
138 | ||
139 | protected static void drawAsLeftOf1stCharacter(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) { | |
134 | 140 | StyleRange style= null; |
135 | 141 | try { |
136 | style= textWidget.getStyleRangeAtOffset(offset); | |
142 | style= textWidget.getStyleRangeAtOffset(widgetOffset); | |
137 | 143 | } catch (Exception e) { |
138 | 144 | return; |
139 | 145 | } |
146 | 152 | return; |
147 | 153 | } |
148 | 154 | if (gc != null) { |
149 | String s= textWidget.getText(offset, offset); | |
150 | boolean isEndOfLine= ("\r".equals(s) || "\n".equals(s)); //$NON-NLS-1$ //$NON-NLS-2$ | |
155 | String hostCharacter= textWidget.getText(widgetOffset, widgetOffset); | |
156 | boolean isEndOfLine= ("\r".equals(hostCharacter) || "\n".equals(hostCharacter)); //$NON-NLS-1$ //$NON-NLS-2$ | |
151 | 157 | |
152 | 158 | // Compute the location of the annotation |
153 | Rectangle bounds= textWidget.getTextBounds(offset, offset); | |
159 | Rectangle bounds= textWidget.getTextBounds(widgetOffset, widgetOffset); | |
154 | 160 | int x= bounds.x + (isEndOfLine ? bounds.width * 2 : 0); |
155 | 161 | int y= bounds.y; |
156 | 162 | |
159 | 165 | |
160 | 166 | // Draw the line content annotation |
161 | 167 | annotation.setLocation(x, y); |
162 | annotation.draw(gc, textWidget, offset, length, color, x, y); | |
168 | annotation.draw(gc, textWidget, widgetOffset, length, color, x, y); | |
163 | 169 | int width= annotation.getWidth(); |
164 | 170 | if (width != 0) { |
165 | 171 | if (isEndOfLine) { |
170 | 176 | } |
171 | 177 | } else { |
172 | 178 | // Get size of the character where GlyphMetrics width is added |
173 | Point charBounds= gc.stringExtent(s); | |
179 | Point charBounds= gc.stringExtent(hostCharacter); | |
174 | 180 | int charWidth= charBounds.x; |
175 | 181 | |
176 | 182 | // FIXME: remove this code when we need not redraw the character (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=531769) |
179 | 185 | // END TO REMOVE |
180 | 186 | |
181 | 187 | // Annotation takes place, add GlyphMetrics width to the style |
182 | StyleRange newStyle= updateStyle(annotation, style); | |
188 | StyleRange newStyle= annotation.updateStyle(style); | |
183 | 189 | if (newStyle != null) { |
184 | 190 | textWidget.setStyleRange(newStyle); |
185 | 191 | return; |
191 | 197 | // GlyphMetrics |
192 | 198 | // Here we need to redraw this first character because GlyphMetrics clip this |
193 | 199 | // character. |
194 | int charX= x + bounds.width - charWidth; | |
195 | int charY= y; | |
200 | int redrawnHostCharX= x + bounds.width - charWidth; | |
201 | int redrawnHostCharY= y; | |
202 | gc.setForeground(textWidget.getForeground()); | |
203 | gc.setBackground(textWidget.getBackground()); | |
204 | gc.setFont(textWidget.getFont()); | |
196 | 205 | if (style != null) { |
197 | 206 | if (style.background != null) { |
198 | 207 | gc.setBackground(style.background); |
199 | gc.fillRectangle(charX, charY, charWidth + 1, bounds.height); | |
208 | gc.fillRectangle(redrawnHostCharX, redrawnHostCharY, charWidth + 1, bounds.height); | |
200 | 209 | } |
201 | 210 | if (style.foreground != null) { |
202 | 211 | gc.setForeground(style.foreground); |
203 | } else { | |
204 | gc.setForeground(textWidget.getForeground()); | |
205 | 212 | } |
206 | gc.setFont(annotation.getFont(style.fontStyle)); | |
207 | } | |
208 | gc.drawString(s, charX, charY, true); | |
213 | if (style.font != null) { | |
214 | gc.setFont(style.font); | |
215 | } | |
216 | } | |
217 | if (textWidget.getSelection().x <= widgetOffset && textWidget.getSelection().y > widgetOffset) { | |
218 | gc.setForeground(textWidget.getSelectionForeground()); | |
219 | gc.setBackground(textWidget.getSelectionBackground()); | |
220 | } | |
221 | gc.drawString(hostCharacter, redrawnHostCharX, redrawnHostCharY, true); | |
209 | 222 | } |
210 | 223 | // END TO REMOVE |
211 | 224 | } else if (style != null && style.metrics != null && style.metrics.width != 0) { |
214 | 227 | textWidget.setStyleRange(style); |
215 | 228 | } |
216 | 229 | } else { |
217 | textWidget.redrawRange(offset, length, true); | |
218 | } | |
219 | } | |
220 | ||
221 | /** | |
222 | * Returns the style to apply with GlyphMetrics width only if needed. | |
223 | * | |
224 | * @param annotation the line content annotation | |
225 | * @param style the current style and null otherwise. | |
226 | * @return the style to apply with GlyphMetrics width only if needed. | |
227 | */ | |
228 | static StyleRange updateStyle(LineContentAnnotation annotation, StyleRange style) { | |
229 | int width= annotation.getWidth(); | |
230 | if (width == 0 || annotation.getRedrawnCharacterWidth() == 0) { | |
231 | return null; | |
232 | } | |
233 | int fullWidth= width + annotation.getRedrawnCharacterWidth(); | |
234 | if (style == null) { | |
235 | style= new StyleRange(); | |
236 | Position position= annotation.getPosition(); | |
237 | style.start= position.getOffset(); | |
238 | style.length= 1; | |
239 | } | |
240 | GlyphMetrics metrics= style.metrics; | |
241 | if (!annotation.isMarkedDeleted()) { | |
242 | if (metrics == null) { | |
243 | metrics= new GlyphMetrics(0, 0, fullWidth); | |
244 | } else { | |
245 | if (metrics.width == fullWidth) { | |
246 | return null; | |
247 | } | |
248 | /** | |
249 | * We must create a new GlyphMetrics instance because comparison with similarTo used | |
250 | * later in StyledText#setStyleRange will compare the same (modified) and won't | |
251 | * realize an update happened. | |
252 | */ | |
253 | metrics= new GlyphMetrics(0, 0, fullWidth); | |
254 | } | |
255 | } else { | |
256 | metrics= null; | |
257 | } | |
258 | style.metrics= metrics; | |
259 | return style; | |
230 | textWidget.redrawRange(widgetOffset, length, true); | |
231 | } | |
232 | } | |
233 | ||
234 | protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) { | |
235 | StyleRange style= null; | |
236 | try { | |
237 | style= textWidget.getStyleRangeAtOffset(widgetOffset - 1); | |
238 | } catch (Exception e) { | |
239 | return; | |
240 | } | |
241 | if (isDeleted(annotation)) { | |
242 | // When annotation is deleted, update metrics to null to remove extra spaces of the line content annotation. | |
243 | if (style != null && style.metrics != null) { | |
244 | style.metrics= null; | |
245 | textWidget.setStyleRange(style); | |
246 | } | |
247 | return; | |
248 | } | |
249 | if (gc != null) { | |
250 | String hostCharacter= textWidget.getText(widgetOffset - 1, widgetOffset - 1); | |
251 | int redrawnCharacterWidth= gc.stringExtent(hostCharacter).x; | |
252 | Rectangle charBounds= textWidget.getTextBounds(widgetOffset - 1, widgetOffset - 1); | |
253 | Rectangle annotationBounds= new Rectangle(charBounds.x + redrawnCharacterWidth, charBounds.y, annotation.getWidth(), charBounds.height); | |
254 | ||
255 | // When line text has line header annotation, there is a space on the top, adjust the y by using char height | |
256 | annotationBounds.y+= charBounds.height - textWidget.getLineHeight(); | |
257 | ||
258 | // Draw the line content annotation | |
259 | annotation.setLocation(annotationBounds.x, annotationBounds.y); | |
260 | annotation.draw(gc, textWidget, widgetOffset, length, color, annotationBounds.x, annotationBounds.y); | |
261 | int width= annotation.getWidth(); | |
262 | if (width != 0) { | |
263 | // FIXME: remove this code when we need not redraw the character (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=531769) | |
264 | // START TO REMOVE | |
265 | annotation.setRedrawnCharacterWidth(redrawnCharacterWidth); | |
266 | // END TO REMOVE | |
267 | ||
268 | // Annotation takes place, add GlyphMetrics width to the style | |
269 | StyleRange newStyle= annotation.updateStyle(style); | |
270 | if (newStyle != null) { | |
271 | textWidget.setStyleRange(newStyle); | |
272 | return; | |
273 | } | |
274 | ||
275 | // FIXME: remove this code when we need not redraw the character (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=531769) | |
276 | // START TO REMOVE | |
277 | // The inline annotation replaces one character by taking a place width | |
278 | // GlyphMetrics | |
279 | // Here we need to redraw this first character because GlyphMetrics clip this | |
280 | // character. | |
281 | gc.setForeground(textWidget.getForeground()); | |
282 | gc.setBackground(textWidget.getBackground()); | |
283 | gc.setFont(textWidget.getFont()); | |
284 | if (style != null) { | |
285 | if (style.background != null) { | |
286 | gc.setBackground(style.background); | |
287 | gc.fillRectangle(charBounds.x, annotationBounds.y, redrawnCharacterWidth, charBounds.height); | |
288 | } | |
289 | if (style.foreground != null) { | |
290 | gc.setForeground(style.foreground); | |
291 | } | |
292 | if (style.font != null) { | |
293 | gc.setFont(style.font); | |
294 | } | |
295 | } | |
296 | int toRedrawCharOffset= widgetOffset - 1; | |
297 | if (textWidget.getSelection().x <= toRedrawCharOffset && textWidget.getSelection().y > toRedrawCharOffset) { | |
298 | gc.setForeground(textWidget.getSelectionForeground()); | |
299 | gc.setBackground(textWidget.getSelectionBackground()); | |
300 | } | |
301 | gc.drawString(hostCharacter, charBounds.x, charBounds.y, true); | |
302 | // END TO REMOVE | |
303 | } else if (style != null && style.metrics != null && style.metrics.width != 0) { | |
304 | // line content annotation had an , reset it | |
305 | style.metrics= null; | |
306 | textWidget.setStyleRange(style); | |
307 | } | |
308 | } else { | |
309 | textWidget.redrawRange(widgetOffset, length, true); | |
310 | } | |
260 | 311 | } |
261 | 312 | |
262 | 313 | /** |
+9
-1
47 | 47 | import org.eclipse.jface.text.ISynchronizable; |
48 | 48 | import org.eclipse.jface.text.ITextPresentationListener; |
49 | 49 | import org.eclipse.jface.text.ITextViewerExtension4; |
50 | import org.eclipse.jface.text.ITextViewerExtension5; | |
50 | 51 | import org.eclipse.jface.text.IViewportListener; |
51 | 52 | import org.eclipse.jface.text.JFaceTextUtil; |
52 | 53 | import org.eclipse.jface.text.Position; |
54 | import org.eclipse.jface.text.Region; | |
53 | 55 | import org.eclipse.jface.text.TextPresentation; |
54 | 56 | import org.eclipse.jface.text.source.Annotation; |
55 | 57 | import org.eclipse.jface.text.source.AnnotationPainter; |
106 | 108 | .forEachRemaining(annotation -> { |
107 | 109 | if (annotation instanceof LineContentAnnotation) { |
108 | 110 | LineContentAnnotation ann= (LineContentAnnotation) annotation; |
109 | StyleRange style= InlinedAnnotationDrawingStrategy.updateStyle(ann, null); | |
111 | StyleRange style= ann.updateStyle(null); | |
110 | 112 | if (style != null) { |
113 | if (fViewer instanceof ITextViewerExtension5) { | |
114 | ITextViewerExtension5 projectionViewer= (ITextViewerExtension5) fViewer; | |
115 | IRegion annotationRegion= projectionViewer.widgetRange2ModelRange(new Region(style.start, style.length)); | |
116 | style.start= annotationRegion.getOffset(); | |
117 | style.length= annotationRegion.getLength(); | |
118 | } | |
111 | 119 | textPresentation.mergeStyleRange(style); |
112 | 120 | } |
113 | 121 | } |
+154
-97
0 | /** | |
1 | * Copyright (c) 2017, 2018 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017, 2018 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.source.inlined; | |
14 | ||
15 | import org.eclipse.swt.custom.StyledText; | |
16 | import org.eclipse.swt.graphics.Color; | |
17 | import org.eclipse.swt.graphics.GC; | |
18 | ||
19 | import org.eclipse.jface.text.Position; | |
20 | import org.eclipse.jface.text.source.ISourceViewer; | |
21 | ||
22 | /** | |
23 | * Inlined annotation which is drawn in the line content and which takes some place with a given | |
24 | * width. | |
25 | * | |
26 | * @since 3.13 | |
27 | */ | |
28 | public class LineContentAnnotation extends AbstractInlinedAnnotation { | |
29 | ||
30 | /** | |
31 | * The annotation width | |
32 | */ | |
33 | private int width; | |
34 | ||
35 | private int redrawnCharacterWidth; | |
36 | ||
37 | /** | |
38 | * Line content annotation constructor. | |
39 | * | |
40 | * @param position the position where the annotation must be drawn. | |
41 | * @param viewer the {@link ISourceViewer} where the annotation must be drawn. | |
42 | */ | |
43 | public LineContentAnnotation(Position position, ISourceViewer viewer) { | |
44 | super(position, viewer); | |
45 | } | |
46 | ||
47 | /** | |
48 | * Returns the annotation width. By default it computes the well width for the text annotation. | |
49 | * | |
50 | * @return the annotation width. | |
51 | */ | |
52 | public final int getWidth() { | |
53 | return width; | |
54 | } | |
55 | ||
56 | /** | |
57 | * {@inheritDoc} | |
58 | * <p> | |
59 | * After drawn, compute the text width and update it. | |
60 | * </p> | |
61 | */ | |
62 | @Override | |
63 | public final void draw(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) { | |
64 | width= drawAndComputeWidth(gc, textWidget, offset, length, color, x, y); | |
65 | } | |
66 | ||
67 | /** | |
68 | * Draw the inlined annotation. By default it draws the text of the annotation with gray color. | |
69 | * User can override this method to draw anything. | |
70 | * | |
71 | * @param gc the graphics context | |
72 | * @param textWidget the text widget to draw on | |
73 | * @param offset the offset of the line | |
74 | * @param length the length of the line | |
75 | * @param color the color of the line | |
76 | * @param x the x position of the annotation | |
77 | * @param y the y position of the annotation | |
78 | * @return the text width. | |
79 | */ | |
80 | protected int drawAndComputeWidth(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) { | |
81 | // Draw the text annotation and returns the width | |
82 | super.draw(gc, textWidget, offset, length, color, x, y); | |
83 | return (int) (gc.stringExtent(getText()).x + 2 * gc.getFontMetrics().getAverageCharacterWidth()); | |
84 | } | |
85 | ||
86 | int getRedrawnCharacterWidth() { | |
87 | return redrawnCharacterWidth; | |
88 | } | |
89 | ||
90 | void setRedrawnCharacterWidth(int redrawnCharacterWidth) { | |
91 | this.redrawnCharacterWidth= redrawnCharacterWidth; | |
92 | } | |
93 | ||
94 | @Override | |
95 | boolean contains(int x, int y) { | |
96 | return (x >= this.fX && x <= this.fX + width && y >= this.fY && y <= this.fY + getTextWidget().getLineHeight()); | |
97 | } | |
98 | ||
99 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.source.inlined; | |
14 | ||
15 | import org.eclipse.swt.custom.StyleRange; | |
16 | import org.eclipse.swt.custom.StyledText; | |
17 | import org.eclipse.swt.graphics.Color; | |
18 | import org.eclipse.swt.graphics.GC; | |
19 | import org.eclipse.swt.graphics.GlyphMetrics; | |
20 | ||
21 | import org.eclipse.jface.text.ITextViewerExtension5; | |
22 | import org.eclipse.jface.text.Position; | |
23 | import org.eclipse.jface.text.TextPresentation; | |
24 | import org.eclipse.jface.text.source.ISourceViewer; | |
25 | ||
26 | /** | |
27 | * Inlined annotation which is drawn in the line content and which takes some place with a given | |
28 | * width. | |
29 | * | |
30 | * @since 3.13 | |
31 | */ | |
32 | public class LineContentAnnotation extends AbstractInlinedAnnotation { | |
33 | ||
34 | /** | |
35 | * The annotation width | |
36 | */ | |
37 | private int width; | |
38 | ||
39 | private int redrawnCharacterWidth; | |
40 | ||
41 | /** | |
42 | * Line content annotation constructor. | |
43 | * | |
44 | * @param position the position where the annotation must be drawn. | |
45 | * @param viewer the {@link ISourceViewer} where the annotation must be drawn. | |
46 | */ | |
47 | public LineContentAnnotation(Position position, ISourceViewer viewer) { | |
48 | super(position, viewer); | |
49 | } | |
50 | ||
51 | /** | |
52 | * Returns the annotation width. By default it computes the well width for the text annotation. | |
53 | * | |
54 | * @return the annotation width. | |
55 | */ | |
56 | public final int getWidth() { | |
57 | return width; | |
58 | } | |
59 | ||
60 | /** | |
61 | * {@inheritDoc} | |
62 | * <p> | |
63 | * After drawn, compute the text width and update it. | |
64 | * </p> | |
65 | */ | |
66 | @Override | |
67 | public final void draw(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) { | |
68 | width= drawAndComputeWidth(gc, textWidget, offset, length, color, x, y); | |
69 | } | |
70 | ||
71 | /** | |
72 | * Draw the inlined annotation. By default it draws the text of the annotation with gray color. | |
73 | * User can override this method to draw anything. | |
74 | * | |
75 | * @param gc the graphics context | |
76 | * @param textWidget the text widget to draw on | |
77 | * @param offset the offset of the line | |
78 | * @param length the length of the line | |
79 | * @param color the color of the line | |
80 | * @param x the x position of the annotation | |
81 | * @param y the y position of the annotation | |
82 | * @return the text width. | |
83 | */ | |
84 | protected int drawAndComputeWidth(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) { | |
85 | // Draw the text annotation and returns the width | |
86 | super.draw(gc, textWidget, offset, length, color, x, y); | |
87 | return (int) (gc.stringExtent(getText()).x + 2 * gc.getFontMetrics().getAverageCharacterWidth()); | |
88 | } | |
89 | ||
90 | int getRedrawnCharacterWidth() { | |
91 | return redrawnCharacterWidth; | |
92 | } | |
93 | ||
94 | void setRedrawnCharacterWidth(int redrawnCharacterWidth) { | |
95 | this.redrawnCharacterWidth= redrawnCharacterWidth; | |
96 | } | |
97 | ||
98 | @Override | |
99 | boolean contains(int x, int y) { | |
100 | return (x >= this.fX && x <= this.fX + width && y >= this.fY && y <= this.fY + getTextWidget().getLineHeight()); | |
101 | } | |
102 | ||
103 | /** | |
104 | * Returns the style to apply with GlyphMetrics width only if needed. | |
105 | * | |
106 | * As it's using Widget position, the results can be passed directly to | |
107 | * {@link StyledText#setStyleRange(StyleRange)} and family. However, in case of a Viewer | |
108 | * providing project/folder with {@link ITextViewerExtension5}, the range must be transformed to | |
109 | * model position before passing it to a {@link TextPresentation}. | |
110 | * | |
111 | * @param style the current style and null otherwise. | |
112 | * @return the style to apply with GlyphMetrics width only if needed. It uses widget position, | |
113 | * not model position. | |
114 | */ | |
115 | StyleRange updateStyle(StyleRange style) { | |
116 | Position widgetPosition= computeWidgetPosition(); | |
117 | boolean usePreviousChar= drawRightToPreviousChar(widgetPosition.getOffset()); | |
118 | if (width == 0 || getRedrawnCharacterWidth() == 0) { | |
119 | return null; | |
120 | } | |
121 | int fullWidth= width + getRedrawnCharacterWidth(); | |
122 | if (style == null) { | |
123 | style= new StyleRange(); | |
124 | style.start= widgetPosition.getOffset(); | |
125 | if (usePreviousChar) { | |
126 | style.start--; | |
127 | } | |
128 | style.length= 1; | |
129 | } | |
130 | GlyphMetrics metrics= style.metrics; | |
131 | if (!isMarkedDeleted()) { | |
132 | if (metrics == null) { | |
133 | metrics= new GlyphMetrics(0, 0, fullWidth); | |
134 | } else { | |
135 | if (metrics.width == fullWidth) { | |
136 | return null; | |
137 | } | |
138 | /** | |
139 | * We must create a new GlyphMetrics instance because comparison with similarTo used | |
140 | * later in StyledText#setStyleRange will compare the same (modified) and won't | |
141 | * realize an update happened. | |
142 | */ | |
143 | metrics= new GlyphMetrics(0, 0, fullWidth); | |
144 | } | |
145 | } else { | |
146 | metrics= null; | |
147 | } | |
148 | style.metrics= metrics; | |
149 | return style; | |
150 | } | |
151 | ||
152 | boolean drawRightToPreviousChar(int widgetOffset) { | |
153 | return widgetOffset > 0 && getTextWidget().getLineAtOffset(widgetOffset) == getTextWidget().getLineAtOffset(widgetOffset - 1); | |
154 | } | |
155 | ||
156 | } |
+55
-55
0 | /** | |
1 | * Copyright (c) 2017, 2018 Angelo ZERR. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.source.inlined; | |
14 | ||
15 | import org.eclipse.swt.custom.StyledText; | |
16 | ||
17 | import org.eclipse.jface.text.Position; | |
18 | import org.eclipse.jface.text.source.ISourceViewer; | |
19 | ||
20 | /** | |
21 | * Inlined annotation which is drawn before a line and which takes some place with a given height. | |
22 | * | |
23 | * @since 3.13 | |
24 | */ | |
25 | public class LineHeaderAnnotation extends AbstractInlinedAnnotation { | |
26 | ||
27 | int oldLine; | |
28 | ||
29 | /** | |
30 | * Line header annotation constructor. | |
31 | * | |
32 | * @param position the position where the annotation must be drawn. | |
33 | * @param viewer the {@link ISourceViewer} where the annotation must be drawn. | |
34 | */ | |
35 | public LineHeaderAnnotation(Position position, ISourceViewer viewer) { | |
36 | super(position, viewer); | |
37 | oldLine= -1; | |
38 | } | |
39 | ||
40 | /** | |
41 | * Returns the annotation height. By default, returns the {@link StyledText#getLineHeight()}. | |
42 | * | |
43 | * @return the annotation height. | |
44 | */ | |
45 | public int getHeight() { | |
46 | StyledText styledText= super.getTextWidget(); | |
47 | return styledText.getLineHeight(); | |
48 | } | |
49 | ||
50 | @Override | |
51 | boolean contains(int x, int y) { | |
52 | return (x >= this.fX && y >= this.fY && y <= this.fY + getHeight()); | |
53 | } | |
54 | } | |
0 | /** | |
1 | * Copyright (c) 2017, 2018 Angelo ZERR. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.source.inlined; | |
14 | ||
15 | import org.eclipse.swt.custom.StyledText; | |
16 | ||
17 | import org.eclipse.jface.text.Position; | |
18 | import org.eclipse.jface.text.source.ISourceViewer; | |
19 | ||
20 | /** | |
21 | * Inlined annotation which is drawn before a line and which takes some place with a given height. | |
22 | * | |
23 | * @since 3.13 | |
24 | */ | |
25 | public class LineHeaderAnnotation extends AbstractInlinedAnnotation { | |
26 | ||
27 | int oldLine; | |
28 | ||
29 | /** | |
30 | * Line header annotation constructor. | |
31 | * | |
32 | * @param position the position where the annotation must be drawn. | |
33 | * @param viewer the {@link ISourceViewer} where the annotation must be drawn. | |
34 | */ | |
35 | public LineHeaderAnnotation(Position position, ISourceViewer viewer) { | |
36 | super(position, viewer); | |
37 | oldLine= -1; | |
38 | } | |
39 | ||
40 | /** | |
41 | * Returns the annotation height. By default, returns the {@link StyledText#getLineHeight()}. | |
42 | * | |
43 | * @return the annotation height. | |
44 | */ | |
45 | public int getHeight() { | |
46 | StyledText styledText= super.getTextWidget(); | |
47 | return styledText.getLineHeight(); | |
48 | } | |
49 | ||
50 | @Override | |
51 | boolean contains(int x, int y) { | |
52 | return (x >= this.fX && y >= this.fY && y <= this.fY + getHeight()); | |
53 | } | |
54 | } |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.source.inlined; | |
14 | ||
15 | import org.eclipse.jface.text.BadLocationException; | |
16 | import org.eclipse.jface.text.IDocument; | |
17 | import org.eclipse.jface.text.Position; | |
18 | ||
19 | /** | |
20 | * Utilities class to retrieve position. | |
21 | * | |
22 | * @since 3.13 | |
23 | */ | |
24 | public class Positions { | |
25 | ||
26 | /** | |
27 | * Returns the line position by taking care or not of of leading spaces. | |
28 | * | |
29 | * @param lineIndex the line index | |
30 | * @param document the document | |
31 | * @param leadingSpaces true if line spacing must take care of and not otherwise. | |
32 | * @return the line position by taking care of leading spaces. | |
33 | * @throws BadLocationException if the line number is invalid in this document | |
34 | */ | |
35 | public static Position of(int lineIndex, IDocument document, boolean leadingSpaces) throws BadLocationException { | |
36 | int offset= document.getLineOffset(lineIndex); | |
37 | int lineLength= document.getLineLength(lineIndex); | |
38 | String line= document.get(offset, lineLength); | |
39 | if (leadingSpaces) { | |
40 | offset+= getLeadingSpaces(line); | |
41 | } | |
42 | return new Position(offset, 1); | |
43 | } | |
44 | ||
45 | /** | |
46 | * Returns the leading spaces of the given line text. | |
47 | * | |
48 | * @param line the line text. | |
49 | * @return the leading spaces of the given line text. | |
50 | */ | |
51 | private static int getLeadingSpaces(String line) { | |
52 | int counter= 0; | |
53 | char[] chars= line.toCharArray(); | |
54 | for (char c : chars) { | |
55 | if (c == '\t') | |
56 | counter++; | |
57 | else if (c == ' ') | |
58 | counter++; | |
59 | else | |
60 | break; | |
61 | } | |
62 | return counter; | |
63 | } | |
64 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.source.inlined; | |
14 | ||
15 | import org.eclipse.jface.text.BadLocationException; | |
16 | import org.eclipse.jface.text.IDocument; | |
17 | import org.eclipse.jface.text.Position; | |
18 | ||
19 | /** | |
20 | * Utilities class to retrieve position. | |
21 | * | |
22 | * @since 3.13 | |
23 | */ | |
24 | public class Positions { | |
25 | ||
26 | /** | |
27 | * Returns the line position by taking care or not of of leading spaces. | |
28 | * | |
29 | * @param lineIndex the line index | |
30 | * @param document the document | |
31 | * @param leadingSpaces true if line spacing must take care of and not otherwise. | |
32 | * @return the line position by taking care of leading spaces. | |
33 | * @throws BadLocationException if the line number is invalid in this document | |
34 | */ | |
35 | public static Position of(int lineIndex, IDocument document, boolean leadingSpaces) throws BadLocationException { | |
36 | int offset= document.getLineOffset(lineIndex); | |
37 | int lineLength= document.getLineLength(lineIndex); | |
38 | String line= document.get(offset, lineLength); | |
39 | if (leadingSpaces) { | |
40 | offset+= getLeadingSpaces(line); | |
41 | } | |
42 | return new Position(offset, 1); | |
43 | } | |
44 | ||
45 | /** | |
46 | * Returns the leading spaces of the given line text. | |
47 | * | |
48 | * @param line the line text. | |
49 | * @return the leading spaces of the given line text. | |
50 | */ | |
51 | private static int getLeadingSpaces(String line) { | |
52 | int counter= 0; | |
53 | char[] chars= line.toCharArray(); | |
54 | for (char c : chars) { | |
55 | if (c == '\t') | |
56 | counter++; | |
57 | else if (c == ' ') | |
58 | counter++; | |
59 | else | |
60 | break; | |
61 | } | |
62 | return counter; | |
63 | } | |
64 | } |
280 | 280 | |
281 | 281 | private int getCaretOffset(TemplateBuffer buffer) { |
282 | 282 | |
283 | TemplateVariable[] variables= buffer.getVariables(); | |
283 | TemplateVariable[] variables= buffer.getVariables(); | |
284 | 284 | for (int i= 0; i != variables.length; i++) { |
285 | 285 | TemplateVariable variable= variables[i]; |
286 | 286 | if (variable.getType().equals(GlobalTemplateVariables.Cursor.NAME)) |
335 | 335 | |
336 | 336 | @Override |
337 | 337 | public String getAdditionalProposalInfo() { |
338 | try { | |
339 | fContext.setReadOnly(true); | |
338 | try { | |
339 | fContext.setReadOnly(true); | |
340 | 340 | TemplateBuffer templateBuffer; |
341 | 341 | try { |
342 | 342 | templateBuffer= fContext.evaluate(fTemplate); |
346 | 346 | |
347 | 347 | return templateBuffer.getString(); |
348 | 348 | |
349 | } catch (BadLocationException e) { | |
349 | } catch (BadLocationException e) { | |
350 | 350 | return null; |
351 | 351 | } |
352 | 352 | } |
13 | 13 | <parent> |
14 | 14 | <artifactId>eclipse.platform.text</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | </parent> |
18 | 18 | <groupId>org.eclipse.core</groupId> |
19 | 19 | <artifactId>org.eclipse.jface.text.examples</artifactId> |
+47
-47
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import org.eclipse.jface.text.BadLocationException; | |
16 | import org.eclipse.jface.text.IDocument; | |
17 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
18 | import org.eclipse.jface.text.codemining.LineHeaderCodeMining; | |
19 | ||
20 | /** | |
21 | * Abstract class for class name mining. | |
22 | * | |
23 | */ | |
24 | public abstract class AbstractClassCodeMining extends LineHeaderCodeMining { | |
25 | ||
26 | private final String className; | |
27 | ||
28 | public AbstractClassCodeMining(String className, int afterLineNumber, IDocument document, | |
29 | ICodeMiningProvider resolver) throws BadLocationException { | |
30 | super(afterLineNumber, document, resolver); | |
31 | this.className = className; | |
32 | } | |
33 | ||
34 | public String getClassName() { | |
35 | return className; | |
36 | } | |
37 | ||
38 | public static String getLineText(IDocument document, int line) { | |
39 | try { | |
40 | int lo = document.getLineOffset(line); | |
41 | int ll = document.getLineLength(line); | |
42 | return document.get(lo, ll); | |
43 | } catch (Exception e) { | |
44 | e.printStackTrace(); | |
45 | return null; | |
46 | } | |
47 | } | |
48 | ||
49 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import org.eclipse.jface.text.BadLocationException; | |
16 | import org.eclipse.jface.text.IDocument; | |
17 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
18 | import org.eclipse.jface.text.codemining.LineHeaderCodeMining; | |
19 | ||
20 | /** | |
21 | * Abstract class for class name mining. | |
22 | * | |
23 | */ | |
24 | public abstract class AbstractClassCodeMining extends LineHeaderCodeMining { | |
25 | ||
26 | private final String className; | |
27 | ||
28 | public AbstractClassCodeMining(String className, int afterLineNumber, IDocument document, | |
29 | ICodeMiningProvider resolver) throws BadLocationException { | |
30 | super(afterLineNumber, document, resolver); | |
31 | this.className = className; | |
32 | } | |
33 | ||
34 | public String getClassName() { | |
35 | return className; | |
36 | } | |
37 | ||
38 | public static String getLineText(IDocument document, int line) { | |
39 | try { | |
40 | int lo = document.getLineOffset(line); | |
41 | int ll = document.getLineLength(line); | |
42 | return document.get(lo, ll); | |
43 | } catch (Exception e) { | |
44 | e.printStackTrace(); | |
45 | return null; | |
46 | } | |
47 | } | |
48 | ||
49 | } |
+49
-49
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import java.util.concurrent.CompletableFuture; | |
16 | ||
17 | import org.eclipse.core.runtime.IProgressMonitor; | |
18 | import org.eclipse.jface.text.BadLocationException; | |
19 | import org.eclipse.jface.text.IDocument; | |
20 | import org.eclipse.jface.text.ITextViewer; | |
21 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
22 | ||
23 | /** | |
24 | * Class implementation mining. | |
25 | * | |
26 | */ | |
27 | public class ClassImplementationCodeMining extends AbstractClassCodeMining { | |
28 | ||
29 | public ClassImplementationCodeMining(String className, int afterLineNumber, IDocument document, | |
30 | ICodeMiningProvider provider) throws BadLocationException { | |
31 | super(className, afterLineNumber, document, provider); | |
32 | } | |
33 | ||
34 | @Override | |
35 | protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { | |
36 | return CompletableFuture.runAsync(() -> { | |
37 | IDocument document = viewer.getDocument(); | |
38 | String className = super.getClassName(); | |
39 | int refCount = 0; | |
40 | int lineCount = document.getNumberOfLines(); | |
41 | for (int i = 0; i < lineCount; i++) { | |
42 | // check if request was canceled. | |
43 | monitor.isCanceled(); | |
44 | String line = getLineText(document, i); | |
45 | refCount += line.contains("implements " + className) ? 1 : 0; | |
46 | } | |
47 | super.setLabel(refCount + " implementation"); | |
48 | }); | |
49 | } | |
50 | ||
51 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import java.util.concurrent.CompletableFuture; | |
16 | ||
17 | import org.eclipse.core.runtime.IProgressMonitor; | |
18 | import org.eclipse.jface.text.BadLocationException; | |
19 | import org.eclipse.jface.text.IDocument; | |
20 | import org.eclipse.jface.text.ITextViewer; | |
21 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
22 | ||
23 | /** | |
24 | * Class implementation mining. | |
25 | * | |
26 | */ | |
27 | public class ClassImplementationCodeMining extends AbstractClassCodeMining { | |
28 | ||
29 | public ClassImplementationCodeMining(String className, int afterLineNumber, IDocument document, | |
30 | ICodeMiningProvider provider) throws BadLocationException { | |
31 | super(className, afterLineNumber, document, provider); | |
32 | } | |
33 | ||
34 | @Override | |
35 | protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { | |
36 | return CompletableFuture.runAsync(() -> { | |
37 | IDocument document = viewer.getDocument(); | |
38 | String className = super.getClassName(); | |
39 | int refCount = 0; | |
40 | int lineCount = document.getNumberOfLines(); | |
41 | for (int i = 0; i < lineCount; i++) { | |
42 | // check if request was canceled. | |
43 | monitor.isCanceled(); | |
44 | String line = getLineText(document, i); | |
45 | refCount += line.contains("implements " + className) ? 1 : 0; | |
46 | } | |
47 | super.setLabel(refCount + " implementation"); | |
48 | }); | |
49 | } | |
50 | ||
51 | } |
+66
-66
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import java.util.ArrayList; | |
16 | import java.util.List; | |
17 | import java.util.concurrent.CompletableFuture; | |
18 | ||
19 | import org.eclipse.core.runtime.IProgressMonitor; | |
20 | import org.eclipse.jface.text.BadLocationException; | |
21 | import org.eclipse.jface.text.IDocument; | |
22 | import org.eclipse.jface.text.ITextViewer; | |
23 | import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; | |
24 | import org.eclipse.jface.text.codemining.ICodeMining; | |
25 | ||
26 | /** | |
27 | * Class implementation mining provider. | |
28 | * | |
29 | */ | |
30 | public class ClassImplementationsCodeMiningProvider extends AbstractCodeMiningProvider { | |
31 | ||
32 | @Override | |
33 | public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, | |
34 | IProgressMonitor monitor) { | |
35 | return CompletableFuture.supplyAsync(() -> { | |
36 | IDocument document = viewer.getDocument(); | |
37 | List<ICodeMining> lenses = new ArrayList<>(); | |
38 | int lineCount = document.getNumberOfLines(); | |
39 | for (int i = 0; i < lineCount; i++) { | |
40 | // check if request was canceled. | |
41 | monitor.isCanceled(); | |
42 | updateContentMining(i, document, "class ", lenses); | |
43 | updateContentMining(i, document, "interface ", lenses); | |
44 | } | |
45 | return lenses; | |
46 | }); | |
47 | } | |
48 | ||
49 | private void updateContentMining(int lineIndex, IDocument document, String token, List<ICodeMining> lenses) { | |
50 | String line = AbstractClassCodeMining.getLineText(document, lineIndex).trim(); | |
51 | int index = line.indexOf(token); | |
52 | if (index == 0) { | |
53 | String className = line.substring(index + token.length(), line.length()); | |
54 | index = className.indexOf(" "); | |
55 | if (index != -1) { | |
56 | className = className.substring(0, index); | |
57 | } | |
58 | if (className.length() > 0) { | |
59 | try { | |
60 | lenses.add(new ClassImplementationCodeMining(className, lineIndex, document, this)); | |
61 | } catch (BadLocationException e) { | |
62 | e.printStackTrace(); | |
63 | } | |
64 | } | |
65 | } | |
66 | } | |
67 | ||
68 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import java.util.ArrayList; | |
16 | import java.util.List; | |
17 | import java.util.concurrent.CompletableFuture; | |
18 | ||
19 | import org.eclipse.core.runtime.IProgressMonitor; | |
20 | import org.eclipse.jface.text.BadLocationException; | |
21 | import org.eclipse.jface.text.IDocument; | |
22 | import org.eclipse.jface.text.ITextViewer; | |
23 | import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; | |
24 | import org.eclipse.jface.text.codemining.ICodeMining; | |
25 | ||
26 | /** | |
27 | * Class implementation mining provider. | |
28 | * | |
29 | */ | |
30 | public class ClassImplementationsCodeMiningProvider extends AbstractCodeMiningProvider { | |
31 | ||
32 | @Override | |
33 | public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, | |
34 | IProgressMonitor monitor) { | |
35 | return CompletableFuture.supplyAsync(() -> { | |
36 | IDocument document = viewer.getDocument(); | |
37 | List<ICodeMining> lenses = new ArrayList<>(); | |
38 | int lineCount = document.getNumberOfLines(); | |
39 | for (int i = 0; i < lineCount; i++) { | |
40 | // check if request was canceled. | |
41 | monitor.isCanceled(); | |
42 | updateContentMining(i, document, "class ", lenses); | |
43 | updateContentMining(i, document, "interface ", lenses); | |
44 | } | |
45 | return lenses; | |
46 | }); | |
47 | } | |
48 | ||
49 | private void updateContentMining(int lineIndex, IDocument document, String token, List<ICodeMining> lenses) { | |
50 | String line = AbstractClassCodeMining.getLineText(document, lineIndex).trim(); | |
51 | int index = line.indexOf(token); | |
52 | if (index == 0) { | |
53 | String className = line.substring(index + token.length(), line.length()); | |
54 | index = className.indexOf(" "); | |
55 | if (index != -1) { | |
56 | className = className.substring(0, index); | |
57 | } | |
58 | if (className.length() > 0) { | |
59 | try { | |
60 | lenses.add(new ClassImplementationCodeMining(className, lineIndex, document, this)); | |
61 | } catch (BadLocationException e) { | |
62 | e.printStackTrace(); | |
63 | } | |
64 | } | |
65 | } | |
66 | } | |
67 | ||
68 | } |
+71
-71
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import java.util.concurrent.CancellationException; | |
16 | import java.util.concurrent.CompletableFuture; | |
17 | ||
18 | import org.eclipse.core.runtime.IProgressMonitor; | |
19 | import org.eclipse.jface.text.BadLocationException; | |
20 | import org.eclipse.jface.text.IDocument; | |
21 | import org.eclipse.jface.text.ITextViewer; | |
22 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
23 | ||
24 | /** | |
25 | * Class reference mining. | |
26 | * | |
27 | */ | |
28 | public class ClassReferenceCodeMining extends AbstractClassCodeMining { | |
29 | ||
30 | private Object lock = new Object(); | |
31 | ||
32 | public ClassReferenceCodeMining(String className, int afterLineNumber, IDocument document, ICodeMiningProvider provider) | |
33 | throws BadLocationException { | |
34 | super(className, afterLineNumber, document, provider); | |
35 | } | |
36 | ||
37 | @Override | |
38 | protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { | |
39 | return CompletableFuture.runAsync(() -> { | |
40 | IDocument document = viewer.getDocument(); | |
41 | String className = super.getClassName(); | |
42 | try { | |
43 | int wait = Integer.parseInt(className); | |
44 | try { | |
45 | for (int i = 0; i < wait; i++) { | |
46 | monitor.isCanceled(); | |
47 | synchronized (lock) { | |
48 | lock.wait(1000); | |
49 | } | |
50 | } | |
51 | } catch (InterruptedException e) { | |
52 | Thread.currentThread().interrupt(); | |
53 | } | |
54 | } catch (NumberFormatException e) { | |
55 | ||
56 | } catch (CancellationException e) { | |
57 | e.printStackTrace(); | |
58 | throw e; | |
59 | } | |
60 | ||
61 | int refCount = 0; | |
62 | int lineCount = document.getNumberOfLines(); | |
63 | for (int i = 0; i < lineCount; i++) { | |
64 | // check if request was canceled. | |
65 | monitor.isCanceled(); | |
66 | String line = getLineText(document, i); | |
67 | refCount += line.contains("new " + className) ? 1 : 0; | |
68 | } | |
69 | super.setLabel(refCount + " references"); | |
70 | }); | |
71 | } | |
72 | ||
73 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import java.util.concurrent.CancellationException; | |
16 | import java.util.concurrent.CompletableFuture; | |
17 | ||
18 | import org.eclipse.core.runtime.IProgressMonitor; | |
19 | import org.eclipse.jface.text.BadLocationException; | |
20 | import org.eclipse.jface.text.IDocument; | |
21 | import org.eclipse.jface.text.ITextViewer; | |
22 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
23 | ||
24 | /** | |
25 | * Class reference mining. | |
26 | * | |
27 | */ | |
28 | public class ClassReferenceCodeMining extends AbstractClassCodeMining { | |
29 | ||
30 | private Object lock = new Object(); | |
31 | ||
32 | public ClassReferenceCodeMining(String className, int afterLineNumber, IDocument document, ICodeMiningProvider provider) | |
33 | throws BadLocationException { | |
34 | super(className, afterLineNumber, document, provider); | |
35 | } | |
36 | ||
37 | @Override | |
38 | protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { | |
39 | return CompletableFuture.runAsync(() -> { | |
40 | IDocument document = viewer.getDocument(); | |
41 | String className = super.getClassName(); | |
42 | try { | |
43 | int wait = Integer.parseInt(className); | |
44 | try { | |
45 | for (int i = 0; i < wait; i++) { | |
46 | monitor.isCanceled(); | |
47 | synchronized (lock) { | |
48 | lock.wait(1000); | |
49 | } | |
50 | } | |
51 | } catch (InterruptedException e) { | |
52 | Thread.currentThread().interrupt(); | |
53 | } | |
54 | } catch (NumberFormatException e) { | |
55 | ||
56 | } catch (CancellationException e) { | |
57 | e.printStackTrace(); | |
58 | throw e; | |
59 | } | |
60 | ||
61 | int refCount = 0; | |
62 | int lineCount = document.getNumberOfLines(); | |
63 | for (int i = 0; i < lineCount; i++) { | |
64 | // check if request was canceled. | |
65 | monitor.isCanceled(); | |
66 | String line = getLineText(document, i); | |
67 | refCount += line.contains("new " + className) ? 1 : 0; | |
68 | } | |
69 | super.setLabel(refCount + " references"); | |
70 | }); | |
71 | } | |
72 | ||
73 | } |
+57
-57
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import java.util.ArrayList; | |
16 | import java.util.List; | |
17 | import java.util.concurrent.CompletableFuture; | |
18 | ||
19 | import org.eclipse.core.runtime.IProgressMonitor; | |
20 | import org.eclipse.jface.text.BadLocationException; | |
21 | import org.eclipse.jface.text.IDocument; | |
22 | import org.eclipse.jface.text.ITextViewer; | |
23 | import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; | |
24 | import org.eclipse.jface.text.codemining.ICodeMining; | |
25 | ||
26 | /** | |
27 | * Class reference mining provider. | |
28 | * | |
29 | */ | |
30 | public class ClassReferenceCodeMiningProvider extends AbstractCodeMiningProvider { | |
31 | ||
32 | @Override | |
33 | public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, | |
34 | IProgressMonitor monitor) { | |
35 | return CompletableFuture.supplyAsync(() -> { | |
36 | IDocument document = viewer.getDocument(); | |
37 | List<ICodeMining> lenses = new ArrayList<>(); | |
38 | int lineCount = document.getNumberOfLines(); | |
39 | for (int i = 0; i < lineCount; i++) { | |
40 | // check if request was canceled. | |
41 | monitor.isCanceled(); | |
42 | String line = AbstractClassCodeMining.getLineText(document, i).trim(); | |
43 | int index = line.indexOf("class "); | |
44 | if (index == 0) { | |
45 | String className = line.substring(index + "class ".length(), line.length()).trim(); | |
46 | if (className.length() > 0) { | |
47 | try { | |
48 | lenses.add(new ClassReferenceCodeMining(className, i, document, this)); | |
49 | } catch (BadLocationException e) { | |
50 | e.printStackTrace(); | |
51 | } | |
52 | } | |
53 | } | |
54 | } | |
55 | return lenses; | |
56 | }); | |
57 | } | |
58 | ||
59 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import java.util.ArrayList; | |
16 | import java.util.List; | |
17 | import java.util.concurrent.CompletableFuture; | |
18 | ||
19 | import org.eclipse.core.runtime.IProgressMonitor; | |
20 | import org.eclipse.jface.text.BadLocationException; | |
21 | import org.eclipse.jface.text.IDocument; | |
22 | import org.eclipse.jface.text.ITextViewer; | |
23 | import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; | |
24 | import org.eclipse.jface.text.codemining.ICodeMining; | |
25 | ||
26 | /** | |
27 | * Class reference mining provider. | |
28 | * | |
29 | */ | |
30 | public class ClassReferenceCodeMiningProvider extends AbstractCodeMiningProvider { | |
31 | ||
32 | @Override | |
33 | public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, | |
34 | IProgressMonitor monitor) { | |
35 | return CompletableFuture.supplyAsync(() -> { | |
36 | IDocument document = viewer.getDocument(); | |
37 | List<ICodeMining> lenses = new ArrayList<>(); | |
38 | int lineCount = document.getNumberOfLines(); | |
39 | for (int i = 0; i < lineCount; i++) { | |
40 | // check if request was canceled. | |
41 | monitor.isCanceled(); | |
42 | String line = AbstractClassCodeMining.getLineText(document, i).trim(); | |
43 | int index = line.indexOf("class "); | |
44 | if (index == 0) { | |
45 | String className = line.substring(index + "class ".length(), line.length()).trim(); | |
46 | if (className.length() > 0) { | |
47 | try { | |
48 | lenses.add(new ClassReferenceCodeMining(className, i, document, this)); | |
49 | } catch (BadLocationException e) { | |
50 | e.printStackTrace(); | |
51 | } | |
52 | } | |
53 | } | |
54 | } | |
55 | return lenses; | |
56 | }); | |
57 | } | |
58 | ||
59 | } |
+109
-109
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import org.eclipse.jface.text.Document; | |
16 | import org.eclipse.jface.text.IDocument; | |
17 | import org.eclipse.jface.text.IRegion; | |
18 | import org.eclipse.jface.text.ITextViewerExtension2; | |
19 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
20 | import org.eclipse.jface.text.reconciler.DirtyRegion; | |
21 | import org.eclipse.jface.text.reconciler.IReconcilingStrategy; | |
22 | import org.eclipse.jface.text.reconciler.MonoReconciler; | |
23 | import org.eclipse.jface.text.source.Annotation; | |
24 | import org.eclipse.jface.text.source.AnnotationModel; | |
25 | import org.eclipse.jface.text.source.AnnotationPainter; | |
26 | import org.eclipse.jface.text.source.IAnnotationAccess; | |
27 | import org.eclipse.jface.text.source.ISourceViewer; | |
28 | import org.eclipse.jface.text.source.ISourceViewerExtension5; | |
29 | import org.eclipse.jface.text.source.SourceViewer; | |
30 | import org.eclipse.swt.SWT; | |
31 | import org.eclipse.swt.layout.FillLayout; | |
32 | import org.eclipse.swt.widgets.Display; | |
33 | import org.eclipse.swt.widgets.Shell; | |
34 | ||
35 | /** | |
36 | * A Code Mining demo with class references and implementations minings. | |
37 | * | |
38 | */ | |
39 | public class CodeMiningDemo { | |
40 | ||
41 | public static void main(String[] args) throws Exception { | |
42 | ||
43 | Display display = new Display(); | |
44 | Shell shell = new Shell(display); | |
45 | shell.setLayout(new FillLayout()); | |
46 | shell.setText("Code Mining demo"); | |
47 | ||
48 | ISourceViewer sourceViewer = new SourceViewer(shell, null, SWT.V_SCROLL | SWT.BORDER); | |
49 | sourceViewer.setDocument( | |
50 | new Document("// Type class & new keyword and see references CodeMining\n" | |
51 | + "// Name class with a number N to emulate Nms before resolving the references CodeMining \n\n" | |
52 | + "class A\n" + "new A\n" + "new A\n\n" + "class 5\n" + "new 5\n" + "new 5\n" + "new 5"), | |
53 | new AnnotationModel()); | |
54 | // Add AnnotationPainter (required by CodeMining) | |
55 | addAnnotationPainter(sourceViewer); | |
56 | // Initialize codemining providers | |
57 | ((ISourceViewerExtension5) sourceViewer).setCodeMiningProviders(new ICodeMiningProvider[] { | |
58 | new ClassReferenceCodeMiningProvider(), new ClassImplementationsCodeMiningProvider() }); | |
59 | // Execute codemining in a reconciler | |
60 | MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() { | |
61 | ||
62 | @Override | |
63 | public void setDocument(IDocument document) { | |
64 | ((ISourceViewerExtension5) sourceViewer).updateCodeMinings(); | |
65 | } | |
66 | ||
67 | @Override | |
68 | public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
69 | ||
70 | } | |
71 | ||
72 | @Override | |
73 | public void reconcile(IRegion partition) { | |
74 | ((ISourceViewerExtension5) sourceViewer).updateCodeMinings(); | |
75 | } | |
76 | }, false); | |
77 | reconciler.install(sourceViewer); | |
78 | ||
79 | shell.open(); | |
80 | while (!shell.isDisposed()) { | |
81 | if (!display.readAndDispatch()) | |
82 | display.sleep(); | |
83 | } | |
84 | display.dispose(); | |
85 | } | |
86 | ||
87 | private static void addAnnotationPainter(ISourceViewer viewer) { | |
88 | IAnnotationAccess annotationAccess = new IAnnotationAccess() { | |
89 | @Override | |
90 | public Object getType(Annotation annotation) { | |
91 | return annotation.getType(); | |
92 | } | |
93 | ||
94 | @Override | |
95 | public boolean isMultiLine(Annotation annotation) { | |
96 | return true; | |
97 | } | |
98 | ||
99 | @Override | |
100 | public boolean isTemporary(Annotation annotation) { | |
101 | return true; | |
102 | } | |
103 | ||
104 | }; | |
105 | AnnotationPainter painter = new AnnotationPainter(viewer, annotationAccess); | |
106 | ((ITextViewerExtension2) viewer).addPainter(painter); | |
107 | // Register this annotation painter as CodeMining annotation painter. | |
108 | ((ISourceViewerExtension5) viewer).setCodeMiningAnnotationPainter(painter); | |
109 | } | |
110 | ||
111 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Add CodeMining support in SourceViewer - Bug 527515 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.codemining; | |
14 | ||
15 | import org.eclipse.jface.text.Document; | |
16 | import org.eclipse.jface.text.IDocument; | |
17 | import org.eclipse.jface.text.IRegion; | |
18 | import org.eclipse.jface.text.ITextViewerExtension2; | |
19 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
20 | import org.eclipse.jface.text.reconciler.DirtyRegion; | |
21 | import org.eclipse.jface.text.reconciler.IReconcilingStrategy; | |
22 | import org.eclipse.jface.text.reconciler.MonoReconciler; | |
23 | import org.eclipse.jface.text.source.Annotation; | |
24 | import org.eclipse.jface.text.source.AnnotationModel; | |
25 | import org.eclipse.jface.text.source.AnnotationPainter; | |
26 | import org.eclipse.jface.text.source.IAnnotationAccess; | |
27 | import org.eclipse.jface.text.source.ISourceViewer; | |
28 | import org.eclipse.jface.text.source.ISourceViewerExtension5; | |
29 | import org.eclipse.jface.text.source.SourceViewer; | |
30 | import org.eclipse.swt.SWT; | |
31 | import org.eclipse.swt.layout.FillLayout; | |
32 | import org.eclipse.swt.widgets.Display; | |
33 | import org.eclipse.swt.widgets.Shell; | |
34 | ||
35 | /** | |
36 | * A Code Mining demo with class references and implementations minings. | |
37 | * | |
38 | */ | |
39 | public class CodeMiningDemo { | |
40 | ||
41 | public static void main(String[] args) throws Exception { | |
42 | ||
43 | Display display = new Display(); | |
44 | Shell shell = new Shell(display); | |
45 | shell.setLayout(new FillLayout()); | |
46 | shell.setText("Code Mining demo"); | |
47 | ||
48 | ISourceViewer sourceViewer = new SourceViewer(shell, null, SWT.V_SCROLL | SWT.BORDER); | |
49 | sourceViewer.setDocument( | |
50 | new Document("// Type class & new keyword and see references CodeMining\n" | |
51 | + "// Name class with a number N to emulate Nms before resolving the references CodeMining \n\n" | |
52 | + "class A\n" + "new A\n" + "new A\n\n" + "class 5\n" + "new 5\n" + "new 5\n" + "new 5"), | |
53 | new AnnotationModel()); | |
54 | // Add AnnotationPainter (required by CodeMining) | |
55 | addAnnotationPainter(sourceViewer); | |
56 | // Initialize codemining providers | |
57 | ((ISourceViewerExtension5) sourceViewer).setCodeMiningProviders(new ICodeMiningProvider[] { | |
58 | new ClassReferenceCodeMiningProvider(), new ClassImplementationsCodeMiningProvider() }); | |
59 | // Execute codemining in a reconciler | |
60 | MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() { | |
61 | ||
62 | @Override | |
63 | public void setDocument(IDocument document) { | |
64 | ((ISourceViewerExtension5) sourceViewer).updateCodeMinings(); | |
65 | } | |
66 | ||
67 | @Override | |
68 | public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
69 | ||
70 | } | |
71 | ||
72 | @Override | |
73 | public void reconcile(IRegion partition) { | |
74 | ((ISourceViewerExtension5) sourceViewer).updateCodeMinings(); | |
75 | } | |
76 | }, false); | |
77 | reconciler.install(sourceViewer); | |
78 | ||
79 | shell.open(); | |
80 | while (!shell.isDisposed()) { | |
81 | if (!display.readAndDispatch()) | |
82 | display.sleep(); | |
83 | } | |
84 | display.dispose(); | |
85 | } | |
86 | ||
87 | private static void addAnnotationPainter(ISourceViewer viewer) { | |
88 | IAnnotationAccess annotationAccess = new IAnnotationAccess() { | |
89 | @Override | |
90 | public Object getType(Annotation annotation) { | |
91 | return annotation.getType(); | |
92 | } | |
93 | ||
94 | @Override | |
95 | public boolean isMultiLine(Annotation annotation) { | |
96 | return true; | |
97 | } | |
98 | ||
99 | @Override | |
100 | public boolean isTemporary(Annotation annotation) { | |
101 | return true; | |
102 | } | |
103 | ||
104 | }; | |
105 | AnnotationPainter painter = new AnnotationPainter(viewer, annotationAccess); | |
106 | ((ITextViewerExtension2) viewer).addPainter(painter); | |
107 | // Register this annotation painter as CodeMining annotation painter. | |
108 | ((ISourceViewerExtension5) viewer).setCodeMiningAnnotationPainter(painter); | |
109 | } | |
110 | ||
111 | } |
+127
-127
0 | /**s | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /**s | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.sources.inlined; | |
14 | ||
15 | import java.util.function.Consumer; | |
16 | ||
17 | import org.eclipse.jface.text.BadLocationException; | |
18 | import org.eclipse.jface.text.IDocument; | |
19 | import org.eclipse.jface.text.IRegion; | |
20 | import org.eclipse.jface.text.Position; | |
21 | import org.eclipse.jface.text.source.ISourceViewer; | |
22 | import org.eclipse.jface.text.source.inlined.LineContentAnnotation; | |
23 | import org.eclipse.jface.util.Geometry; | |
24 | import org.eclipse.swt.custom.StyledText; | |
25 | import org.eclipse.swt.events.MouseEvent; | |
26 | import org.eclipse.swt.graphics.Color; | |
27 | import org.eclipse.swt.graphics.FontMetrics; | |
28 | import org.eclipse.swt.graphics.GC; | |
29 | import org.eclipse.swt.graphics.RGB; | |
30 | import org.eclipse.swt.graphics.Rectangle; | |
31 | import org.eclipse.swt.widgets.ColorDialog; | |
32 | import org.eclipse.swt.widgets.Shell; | |
33 | ||
34 | /** | |
35 | * Color annotation displays a colorized square before the rgb declaration. | |
36 | */ | |
37 | public class ColorAnnotation extends LineContentAnnotation { | |
38 | ||
39 | private Color color; | |
40 | ||
41 | private Consumer<MouseEvent> action = e -> { | |
42 | StyledText styledText = super.getTextWidget(); | |
43 | Shell shell = new Shell(styledText.getDisplay()); | |
44 | Rectangle location = Geometry.toDisplay(styledText, new Rectangle(e.x, e.y, 1, 1)); | |
45 | shell.setLocation(location.x, location.y); | |
46 | // Open color dialog | |
47 | ColorDialog dialog = new ColorDialog(shell); | |
48 | // dialog.setRGB(annotation.getRGBA().rgb); | |
49 | RGB color = dialog.open(); | |
50 | if (color != null) { | |
51 | // Color was selected, update the viewer | |
52 | try { | |
53 | int offset = getPosition().getOffset(); | |
54 | IDocument document = getViewer().getDocument(); | |
55 | IRegion line = document.getLineInformation(document.getLineOfOffset(offset)); | |
56 | int length = line.getLength() - (offset - line.getOffset()); | |
57 | String rgb = formatToRGB(color); | |
58 | document.replace(offset, length, rgb); | |
59 | } catch (BadLocationException e1) { | |
60 | ||
61 | } | |
62 | } | |
63 | }; | |
64 | ||
65 | /** | |
66 | * Format the given rgb to hexa color. | |
67 | * | |
68 | * @param rgb | |
69 | * @return the hexa color from the given rgb. | |
70 | */ | |
71 | private static String formatToRGB(RGB rgb) { | |
72 | return new StringBuilder("rgb(").append(rgb.red).append(",").append(rgb.green).append(",").append(rgb.blue) | |
73 | .append(")").toString(); | |
74 | } | |
75 | ||
76 | public ColorAnnotation(Position pos, ISourceViewer viewer) { | |
77 | super(pos, viewer); | |
78 | } | |
79 | ||
80 | public void setColor(Color color) { | |
81 | this.color = color; | |
82 | } | |
83 | ||
84 | @Override | |
85 | protected int drawAndComputeWidth(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) { | |
86 | FontMetrics fontMetrics = gc.getFontMetrics(); | |
87 | int size = getSquareSize(fontMetrics); | |
88 | x += fontMetrics.getLeading(); | |
89 | y += fontMetrics.getDescent(); | |
90 | ||
91 | Rectangle rect = new Rectangle(x, y, size, size); | |
92 | ||
93 | // Fill square | |
94 | gc.setBackground(this.color); | |
95 | gc.fillRectangle(rect); | |
96 | ||
97 | // Draw square box | |
98 | gc.setForeground(textWidget.getForeground()); | |
99 | gc.drawRectangle(rect); | |
100 | return getSquareWidth(gc.getFontMetrics()); | |
101 | } | |
102 | ||
103 | /** | |
104 | * Returns the colorized square size. | |
105 | * | |
106 | * @param fontMetrics | |
107 | * @return the colorized square size. | |
108 | */ | |
109 | public static int getSquareSize(FontMetrics fontMetrics) { | |
110 | return fontMetrics.getHeight() - 2 * fontMetrics.getDescent(); | |
111 | } | |
112 | ||
113 | /** | |
114 | * Compute width of square | |
115 | * | |
116 | * @param styledText | |
117 | * @return the width of square | |
118 | */ | |
119 | private static int getSquareWidth(FontMetrics fontMetrics) { | |
120 | // width = 2 spaces + size width of square | |
121 | int width = 2 * fontMetrics.getAverageCharWidth() + getSquareSize(fontMetrics); | |
122 | return width; | |
123 | } | |
124 | ||
125 | @Override | |
126 | public Consumer<MouseEvent> getAction(MouseEvent e) { | |
127 | return action; | |
128 | } | |
129 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.sources.inlined; | |
14 | ||
15 | import java.util.function.Consumer; | |
16 | ||
17 | import org.eclipse.jface.text.BadLocationException; | |
18 | import org.eclipse.jface.text.IDocument; | |
19 | import org.eclipse.jface.text.IRegion; | |
20 | import org.eclipse.jface.text.Position; | |
21 | import org.eclipse.jface.text.source.ISourceViewer; | |
22 | import org.eclipse.jface.text.source.inlined.LineContentAnnotation; | |
23 | import org.eclipse.jface.util.Geometry; | |
24 | import org.eclipse.swt.custom.StyledText; | |
25 | import org.eclipse.swt.events.MouseEvent; | |
26 | import org.eclipse.swt.graphics.Color; | |
27 | import org.eclipse.swt.graphics.FontMetrics; | |
28 | import org.eclipse.swt.graphics.GC; | |
29 | import org.eclipse.swt.graphics.RGB; | |
30 | import org.eclipse.swt.graphics.Rectangle; | |
31 | import org.eclipse.swt.widgets.ColorDialog; | |
32 | import org.eclipse.swt.widgets.Shell; | |
33 | ||
34 | /** | |
35 | * Color annotation displays a colorized square before the rgb declaration. | |
36 | */ | |
37 | public class ColorAnnotation extends LineContentAnnotation { | |
38 | ||
39 | private Color color; | |
40 | ||
41 | private Consumer<MouseEvent> action = e -> { | |
42 | StyledText styledText = super.getTextWidget(); | |
43 | Shell shell = new Shell(styledText.getDisplay()); | |
44 | Rectangle location = Geometry.toDisplay(styledText, new Rectangle(e.x, e.y, 1, 1)); | |
45 | shell.setLocation(location.x, location.y); | |
46 | // Open color dialog | |
47 | ColorDialog dialog = new ColorDialog(shell); | |
48 | // dialog.setRGB(annotation.getRGBA().rgb); | |
49 | RGB color = dialog.open(); | |
50 | if (color != null) { | |
51 | // Color was selected, update the viewer | |
52 | try { | |
53 | int offset = getPosition().getOffset(); | |
54 | IDocument document = getViewer().getDocument(); | |
55 | IRegion line = document.getLineInformation(document.getLineOfOffset(offset)); | |
56 | int length = line.getLength() - (offset - line.getOffset()); | |
57 | String rgb = formatToRGB(color); | |
58 | document.replace(offset, length, rgb); | |
59 | } catch (BadLocationException e1) { | |
60 | ||
61 | } | |
62 | } | |
63 | }; | |
64 | ||
65 | /** | |
66 | * Format the given rgb to hexa color. | |
67 | * | |
68 | * @param rgb | |
69 | * @return the hexa color from the given rgb. | |
70 | */ | |
71 | private static String formatToRGB(RGB rgb) { | |
72 | return new StringBuilder("rgb(").append(rgb.red).append(",").append(rgb.green).append(",").append(rgb.blue) | |
73 | .append(")").toString(); | |
74 | } | |
75 | ||
76 | public ColorAnnotation(Position pos, ISourceViewer viewer) { | |
77 | super(pos, viewer); | |
78 | } | |
79 | ||
80 | public void setColor(Color color) { | |
81 | this.color = color; | |
82 | } | |
83 | ||
84 | @Override | |
85 | protected int drawAndComputeWidth(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) { | |
86 | FontMetrics fontMetrics = gc.getFontMetrics(); | |
87 | int size = getSquareSize(fontMetrics); | |
88 | x += fontMetrics.getLeading(); | |
89 | y += fontMetrics.getDescent(); | |
90 | ||
91 | Rectangle rect = new Rectangle(x, y, size, size); | |
92 | ||
93 | // Fill square | |
94 | gc.setBackground(this.color); | |
95 | gc.fillRectangle(rect); | |
96 | ||
97 | // Draw square box | |
98 | gc.setForeground(textWidget.getForeground()); | |
99 | gc.drawRectangle(rect); | |
100 | return getSquareWidth(gc.getFontMetrics()); | |
101 | } | |
102 | ||
103 | /** | |
104 | * Returns the colorized square size. | |
105 | * | |
106 | * @param fontMetrics | |
107 | * @return the colorized square size. | |
108 | */ | |
109 | public static int getSquareSize(FontMetrics fontMetrics) { | |
110 | return fontMetrics.getHeight() - 2 * fontMetrics.getDescent(); | |
111 | } | |
112 | ||
113 | /** | |
114 | * Compute width of square | |
115 | * | |
116 | * @param styledText | |
117 | * @return the width of square | |
118 | */ | |
119 | private static int getSquareWidth(FontMetrics fontMetrics) { | |
120 | // width = 2 spaces + size width of square | |
121 | int width = 2 * fontMetrics.getAverageCharWidth() + getSquareSize(fontMetrics); | |
122 | return width; | |
123 | } | |
124 | ||
125 | @Override | |
126 | public Consumer<MouseEvent> getAction(MouseEvent e) { | |
127 | return action; | |
128 | } | |
129 | } |
+31
-31
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.sources.inlined; | |
14 | ||
15 | import org.eclipse.jface.text.Position; | |
16 | import org.eclipse.jface.text.source.ISourceViewer; | |
17 | import org.eclipse.jface.text.source.inlined.LineHeaderAnnotation; | |
18 | ||
19 | /** | |
20 | * Color status annotation shows the result of rgb parse before each line which | |
21 | * defines 'color:'. | |
22 | */ | |
23 | public class ColorStatusAnnotation extends LineHeaderAnnotation { | |
24 | ||
25 | public ColorStatusAnnotation(Position position, ISourceViewer viewer) { | |
26 | super(position, viewer); | |
27 | } | |
28 | ||
29 | public void setStatus(String status) { | |
30 | super.setText(status); | |
31 | } | |
32 | ||
33 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.sources.inlined; | |
14 | ||
15 | import org.eclipse.jface.text.Position; | |
16 | import org.eclipse.jface.text.source.ISourceViewer; | |
17 | import org.eclipse.jface.text.source.inlined.LineHeaderAnnotation; | |
18 | ||
19 | /** | |
20 | * Color status annotation shows the result of rgb parse before each line which | |
21 | * defines 'color:'. | |
22 | */ | |
23 | public class ColorStatusAnnotation extends LineHeaderAnnotation { | |
24 | ||
25 | public ColorStatusAnnotation(Position position, ISourceViewer viewer) { | |
26 | super(position, viewer); | |
27 | } | |
28 | ||
29 | public void setStatus(String status) { | |
30 | super.setText(status); | |
31 | } | |
32 | ||
33 | } |
+287
-287
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.sources.inlined; | |
14 | ||
15 | import java.util.HashSet; | |
16 | import java.util.Set; | |
17 | import java.util.regex.Matcher; | |
18 | import java.util.regex.Pattern; | |
19 | ||
20 | import org.eclipse.jface.text.BadLocationException; | |
21 | import org.eclipse.jface.text.Document; | |
22 | import org.eclipse.jface.text.IDocument; | |
23 | import org.eclipse.jface.text.IRegion; | |
24 | import org.eclipse.jface.text.ITextViewerExtension2; | |
25 | import org.eclipse.jface.text.Position; | |
26 | import org.eclipse.jface.text.reconciler.DirtyRegion; | |
27 | import org.eclipse.jface.text.reconciler.IReconcilingStrategy; | |
28 | import org.eclipse.jface.text.reconciler.MonoReconciler; | |
29 | import org.eclipse.jface.text.source.Annotation; | |
30 | import org.eclipse.jface.text.source.AnnotationModel; | |
31 | import org.eclipse.jface.text.source.AnnotationPainter; | |
32 | import org.eclipse.jface.text.source.IAnnotationAccess; | |
33 | import org.eclipse.jface.text.source.ISourceViewer; | |
34 | import org.eclipse.jface.text.source.SourceViewer; | |
35 | import org.eclipse.jface.text.source.inlined.AbstractInlinedAnnotation; | |
36 | import org.eclipse.jface.text.source.inlined.InlinedAnnotationSupport; | |
37 | import org.eclipse.jface.text.source.inlined.LineContentAnnotation; | |
38 | import org.eclipse.jface.text.source.inlined.LineHeaderAnnotation; | |
39 | import org.eclipse.jface.text.source.inlined.Positions; | |
40 | import org.eclipse.swt.SWT; | |
41 | import org.eclipse.swt.graphics.Color; | |
42 | import org.eclipse.swt.graphics.Device; | |
43 | import org.eclipse.swt.layout.FillLayout; | |
44 | import org.eclipse.swt.widgets.Display; | |
45 | import org.eclipse.swt.widgets.Shell; | |
46 | ||
47 | /** | |
48 | * An inlined demo with {@link LineHeaderAnnotation} and | |
49 | * {@link LineContentAnnotation} annotations both: | |
50 | * | |
51 | * <ul> | |
52 | * <li>a status OK, NOK is displayed before the line which starts with 'color:'. | |
53 | * This status is the result of the content after 'color' which must be a rgb | |
54 | * content. Here {@link ColorStatusAnnotation} is used.</li> | |
55 | * <li>a colorized square is displayed before the rgb declaration (inside the | |
56 | * line content). Here {@link ColorAnnotation} is used.</li> | |
57 | * </ul> | |
58 | * | |
59 | */ | |
60 | public class InlinedAnnotationDemo { | |
61 | ||
62 | public static void main(String[] args) throws Exception { | |
63 | ||
64 | Display display = new Display(); | |
65 | Shell shell = new Shell(display); | |
66 | shell.setLayout(new FillLayout()); | |
67 | shell.setText("Inlined annotation demo"); | |
68 | ||
69 | // Create source viewer and initialize the content | |
70 | ISourceViewer sourceViewer = new SourceViewer(shell, null, SWT.V_SCROLL | SWT.BORDER); | |
71 | sourceViewer.setDocument(new Document("\ncolor:rgb(255, 255, 0)"), new AnnotationModel()); | |
72 | ||
73 | // Initialize inlined annotations support | |
74 | InlinedAnnotationSupport support = new InlinedAnnotationSupport(); | |
75 | support.install(sourceViewer, createAnnotationPainter(sourceViewer)); | |
76 | ||
77 | // Refresh inlined annotation in none UI Thread with reconciler. | |
78 | MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() { | |
79 | ||
80 | @Override | |
81 | public void setDocument(IDocument document) { | |
82 | Set<AbstractInlinedAnnotation> annotations = getInlinedAnnotation(sourceViewer, support); | |
83 | support.updateAnnotations(annotations); | |
84 | } | |
85 | ||
86 | @Override | |
87 | public void reconcile(IRegion partition) { | |
88 | Set<AbstractInlinedAnnotation> anns = getInlinedAnnotation(sourceViewer, support); | |
89 | support.updateAnnotations(anns); | |
90 | } | |
91 | ||
92 | @Override | |
93 | public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
94 | ||
95 | } | |
96 | }, false); | |
97 | reconciler.setDelay(1); | |
98 | reconciler.install(sourceViewer); | |
99 | ||
100 | shell.open(); | |
101 | while (!shell.isDisposed()) { | |
102 | if (!display.readAndDispatch()) | |
103 | display.sleep(); | |
104 | } | |
105 | display.dispose(); | |
106 | } | |
107 | ||
108 | /** | |
109 | * Create annotation painter. | |
110 | * | |
111 | * @param viewer | |
112 | * the viewer. | |
113 | * @return annotation painter. | |
114 | */ | |
115 | private static AnnotationPainter createAnnotationPainter(ISourceViewer viewer) { | |
116 | IAnnotationAccess annotationAccess = new IAnnotationAccess() { | |
117 | @Override | |
118 | public Object getType(Annotation annotation) { | |
119 | return annotation.getType(); | |
120 | } | |
121 | ||
122 | @Override | |
123 | public boolean isMultiLine(Annotation annotation) { | |
124 | return true; | |
125 | } | |
126 | ||
127 | @Override | |
128 | public boolean isTemporary(Annotation annotation) { | |
129 | return true; | |
130 | } | |
131 | ||
132 | }; | |
133 | AnnotationPainter painter = new AnnotationPainter(viewer, annotationAccess); | |
134 | ((ITextViewerExtension2) viewer).addPainter(painter); | |
135 | return painter; | |
136 | } | |
137 | ||
138 | /** | |
139 | * Returns the inlined annotations list to display in the given viewer. | |
140 | * | |
141 | * @param viewer | |
142 | * the viewer | |
143 | * @param support | |
144 | * the inlined annotation suppor. | |
145 | * @return the inlined annotations list to display in the given viewer. | |
146 | */ | |
147 | private static Set<AbstractInlinedAnnotation> getInlinedAnnotation(ISourceViewer viewer, | |
148 | InlinedAnnotationSupport support) { | |
149 | IDocument document = viewer.getDocument(); | |
150 | Set<AbstractInlinedAnnotation> annotations = new HashSet<>(); | |
151 | int lineCount = document.getNumberOfLines(); | |
152 | for (int i = 0; i < lineCount; i++) { | |
153 | String line = getLineText(document, i).trim(); | |
154 | int index = line.indexOf("color:"); | |
155 | if (index == 0) { | |
156 | String rgb = line.substring(index + "color:".length(), line.length()).trim(); | |
157 | try { | |
158 | String status = "OK!"; | |
159 | Color color = parse(rgb, viewer.getTextWidget().getDisplay()); | |
160 | if (color != null) { | |
161 | } else { | |
162 | status = "ERROR!"; | |
163 | } | |
164 | // Status color annotation | |
165 | Position pos = Positions.of(i, document, true); | |
166 | ColorStatusAnnotation statusAnnotation = support.findExistingAnnotation(pos); | |
167 | if (statusAnnotation == null) { | |
168 | statusAnnotation = new ColorStatusAnnotation(pos, viewer); | |
169 | } | |
170 | statusAnnotation.setStatus(status); | |
171 | annotations.add(statusAnnotation); | |
172 | ||
173 | // Color annotation | |
174 | if (color != null) { | |
175 | Position colorPos = new Position(pos.offset + index + "color:".length(), 1); | |
176 | ColorAnnotation colorAnnotation = support.findExistingAnnotation(colorPos); | |
177 | if (colorAnnotation == null) { | |
178 | colorAnnotation = new ColorAnnotation(colorPos, viewer); | |
179 | } | |
180 | colorAnnotation.setColor(color); | |
181 | annotations.add(colorAnnotation); | |
182 | } | |
183 | ||
184 | // rgb parameter names annotations | |
185 | int rgbIndex = line.indexOf("rgb"); | |
186 | if (rgbIndex != -1) { | |
187 | rgbIndex = rgbIndex + "rgb".length(); | |
188 | int startOffset = pos.offset + rgbIndex; | |
189 | String rgbContent = line.substring(rgbIndex, line.length()); | |
190 | int startIndex = addRGBParamNameAnnotation("red:", rgbContent, 0, startOffset, viewer, support, | |
191 | annotations); | |
192 | if (startIndex != -1) { | |
193 | startIndex = addRGBParamNameAnnotation("green:", rgbContent, startIndex, startOffset, viewer, | |
194 | support, annotations); | |
195 | if (startIndex != -1) { | |
196 | startIndex = addRGBParamNameAnnotation("blue:", rgbContent, startIndex, startOffset, | |
197 | viewer, support, annotations); | |
198 | } | |
199 | } | |
200 | } | |
201 | ||
202 | } catch (BadLocationException e) { | |
203 | e.printStackTrace(); | |
204 | } | |
205 | } | |
206 | } | |
207 | return annotations; | |
208 | } | |
209 | ||
210 | /** | |
211 | * Add RGB parameter name annotation | |
212 | * | |
213 | * @param paramName | |
214 | * @param rgbContent | |
215 | * @param startIndex | |
216 | * @param startOffset | |
217 | * @param viewer | |
218 | * @param support | |
219 | * @param annotations | |
220 | * @return the current parsed index | |
221 | */ | |
222 | private static int addRGBParamNameAnnotation(String paramName, String rgbContent, int startIndex, int startOffset, | |
223 | ISourceViewer viewer, InlinedAnnotationSupport support, Set<AbstractInlinedAnnotation> annotations) { | |
224 | char startChar = startIndex == 0 ? '(' : ','; | |
225 | char[] chars = rgbContent.toCharArray(); | |
226 | for (int i = startIndex; i < chars.length; i++) { | |
227 | char c = chars[i]; | |
228 | if (c == startChar) { | |
229 | if (i == chars.length - 1) { | |
230 | return -1; | |
231 | } | |
232 | Position paramPos = new Position(startOffset + i + 1, 1); | |
233 | LineContentAnnotation colorParamAnnotation = support.findExistingAnnotation(paramPos); | |
234 | if (colorParamAnnotation == null) { | |
235 | colorParamAnnotation = new LineContentAnnotation(paramPos, viewer); | |
236 | } | |
237 | colorParamAnnotation.setText(paramName); | |
238 | annotations.add(colorParamAnnotation); | |
239 | return i + 1; | |
240 | } | |
241 | } | |
242 | return -1; | |
243 | } | |
244 | ||
245 | /** | |
246 | * Parse the given input rgb color and returns an instance of SWT Color and null | |
247 | * otherwise. | |
248 | * | |
249 | * @param input | |
250 | * the rgb string color | |
251 | * @param device | |
252 | * @return the created color and null otherwise. | |
253 | */ | |
254 | private static Color parse(String input, Device device) { | |
255 | Pattern c = Pattern.compile("rgb *\\( *([0-9]+), *([0-9]+), *([0-9]+) *\\)"); | |
256 | Matcher m = c.matcher(input); | |
257 | if (m.matches()) { | |
258 | try { | |
259 | return new Color(device, Integer.valueOf(m.group(1)), // r | |
260 | Integer.valueOf(m.group(2)), // g | |
261 | Integer.valueOf(m.group(3))); // b | |
262 | } catch (Exception e) { | |
263 | ||
264 | } | |
265 | } | |
266 | return null; | |
267 | } | |
268 | ||
269 | /** | |
270 | * Returns the line text. | |
271 | * | |
272 | * @param document | |
273 | * the document. | |
274 | * @param line | |
275 | * the line index. | |
276 | * @return the line text. | |
277 | */ | |
278 | private static String getLineText(IDocument document, int line) { | |
279 | try { | |
280 | int offset = document.getLineOffset(line); | |
281 | int length = document.getLineLength(line); | |
282 | return document.get(offset, length); | |
283 | } catch (Exception e) { | |
284 | e.printStackTrace(); | |
285 | return null; | |
286 | } | |
287 | } | |
288 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 | |
12 | */ | |
13 | package org.eclipse.jface.text.examples.sources.inlined; | |
14 | ||
15 | import java.util.HashSet; | |
16 | import java.util.Set; | |
17 | import java.util.regex.Matcher; | |
18 | import java.util.regex.Pattern; | |
19 | ||
20 | import org.eclipse.jface.text.BadLocationException; | |
21 | import org.eclipse.jface.text.Document; | |
22 | import org.eclipse.jface.text.IDocument; | |
23 | import org.eclipse.jface.text.IRegion; | |
24 | import org.eclipse.jface.text.ITextViewerExtension2; | |
25 | import org.eclipse.jface.text.Position; | |
26 | import org.eclipse.jface.text.reconciler.DirtyRegion; | |
27 | import org.eclipse.jface.text.reconciler.IReconcilingStrategy; | |
28 | import org.eclipse.jface.text.reconciler.MonoReconciler; | |
29 | import org.eclipse.jface.text.source.Annotation; | |
30 | import org.eclipse.jface.text.source.AnnotationModel; | |
31 | import org.eclipse.jface.text.source.AnnotationPainter; | |
32 | import org.eclipse.jface.text.source.IAnnotationAccess; | |
33 | import org.eclipse.jface.text.source.ISourceViewer; | |
34 | import org.eclipse.jface.text.source.SourceViewer; | |
35 | import org.eclipse.jface.text.source.inlined.AbstractInlinedAnnotation; | |
36 | import org.eclipse.jface.text.source.inlined.InlinedAnnotationSupport; | |
37 | import org.eclipse.jface.text.source.inlined.LineContentAnnotation; | |
38 | import org.eclipse.jface.text.source.inlined.LineHeaderAnnotation; | |
39 | import org.eclipse.jface.text.source.inlined.Positions; | |
40 | import org.eclipse.swt.SWT; | |
41 | import org.eclipse.swt.graphics.Color; | |
42 | import org.eclipse.swt.graphics.Device; | |
43 | import org.eclipse.swt.layout.FillLayout; | |
44 | import org.eclipse.swt.widgets.Display; | |
45 | import org.eclipse.swt.widgets.Shell; | |
46 | ||
47 | /** | |
48 | * An inlined demo with {@link LineHeaderAnnotation} and | |
49 | * {@link LineContentAnnotation} annotations both: | |
50 | * | |
51 | * <ul> | |
52 | * <li>a status OK, NOK is displayed before the line which starts with 'color:'. | |
53 | * This status is the result of the content after 'color' which must be a rgb | |
54 | * content. Here {@link ColorStatusAnnotation} is used.</li> | |
55 | * <li>a colorized square is displayed before the rgb declaration (inside the | |
56 | * line content). Here {@link ColorAnnotation} is used.</li> | |
57 | * </ul> | |
58 | * | |
59 | */ | |
60 | public class InlinedAnnotationDemo { | |
61 | ||
62 | public static void main(String[] args) throws Exception { | |
63 | ||
64 | Display display = new Display(); | |
65 | Shell shell = new Shell(display); | |
66 | shell.setLayout(new FillLayout()); | |
67 | shell.setText("Inlined annotation demo"); | |
68 | ||
69 | // Create source viewer and initialize the content | |
70 | ISourceViewer sourceViewer = new SourceViewer(shell, null, SWT.V_SCROLL | SWT.BORDER); | |
71 | sourceViewer.setDocument(new Document("\ncolor:rgb(255, 255, 0)"), new AnnotationModel()); | |
72 | ||
73 | // Initialize inlined annotations support | |
74 | InlinedAnnotationSupport support = new InlinedAnnotationSupport(); | |
75 | support.install(sourceViewer, createAnnotationPainter(sourceViewer)); | |
76 | ||
77 | // Refresh inlined annotation in none UI Thread with reconciler. | |
78 | MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() { | |
79 | ||
80 | @Override | |
81 | public void setDocument(IDocument document) { | |
82 | Set<AbstractInlinedAnnotation> annotations = getInlinedAnnotation(sourceViewer, support); | |
83 | support.updateAnnotations(annotations); | |
84 | } | |
85 | ||
86 | @Override | |
87 | public void reconcile(IRegion partition) { | |
88 | Set<AbstractInlinedAnnotation> anns = getInlinedAnnotation(sourceViewer, support); | |
89 | support.updateAnnotations(anns); | |
90 | } | |
91 | ||
92 | @Override | |
93 | public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
94 | ||
95 | } | |
96 | }, false); | |
97 | reconciler.setDelay(1); | |
98 | reconciler.install(sourceViewer); | |
99 | ||
100 | shell.open(); | |
101 | while (!shell.isDisposed()) { | |
102 | if (!display.readAndDispatch()) | |
103 | display.sleep(); | |
104 | } | |
105 | display.dispose(); | |
106 | } | |
107 | ||
108 | /** | |
109 | * Create annotation painter. | |
110 | * | |
111 | * @param viewer | |
112 | * the viewer. | |
113 | * @return annotation painter. | |
114 | */ | |
115 | private static AnnotationPainter createAnnotationPainter(ISourceViewer viewer) { | |
116 | IAnnotationAccess annotationAccess = new IAnnotationAccess() { | |
117 | @Override | |
118 | public Object getType(Annotation annotation) { | |
119 | return annotation.getType(); | |
120 | } | |
121 | ||
122 | @Override | |
123 | public boolean isMultiLine(Annotation annotation) { | |
124 | return true; | |
125 | } | |
126 | ||
127 | @Override | |
128 | public boolean isTemporary(Annotation annotation) { | |
129 | return true; | |
130 | } | |
131 | ||
132 | }; | |
133 | AnnotationPainter painter = new AnnotationPainter(viewer, annotationAccess); | |
134 | ((ITextViewerExtension2) viewer).addPainter(painter); | |
135 | return painter; | |
136 | } | |
137 | ||
138 | /** | |
139 | * Returns the inlined annotations list to display in the given viewer. | |
140 | * | |
141 | * @param viewer | |
142 | * the viewer | |
143 | * @param support | |
144 | * the inlined annotation suppor. | |
145 | * @return the inlined annotations list to display in the given viewer. | |
146 | */ | |
147 | private static Set<AbstractInlinedAnnotation> getInlinedAnnotation(ISourceViewer viewer, | |
148 | InlinedAnnotationSupport support) { | |
149 | IDocument document = viewer.getDocument(); | |
150 | Set<AbstractInlinedAnnotation> annotations = new HashSet<>(); | |
151 | int lineCount = document.getNumberOfLines(); | |
152 | for (int i = 0; i < lineCount; i++) { | |
153 | String line = getLineText(document, i).trim(); | |
154 | int index = line.indexOf("color:"); | |
155 | if (index == 0) { | |
156 | String rgb = line.substring(index + "color:".length(), line.length()).trim(); | |
157 | try { | |
158 | String status = "OK!"; | |
159 | Color color = parse(rgb, viewer.getTextWidget().getDisplay()); | |
160 | if (color != null) { | |
161 | } else { | |
162 | status = "ERROR!"; | |
163 | } | |
164 | // Status color annotation | |
165 | Position pos = Positions.of(i, document, true); | |
166 | ColorStatusAnnotation statusAnnotation = support.findExistingAnnotation(pos); | |
167 | if (statusAnnotation == null) { | |
168 | statusAnnotation = new ColorStatusAnnotation(pos, viewer); | |
169 | } | |
170 | statusAnnotation.setStatus(status); | |
171 | annotations.add(statusAnnotation); | |
172 | ||
173 | // Color annotation | |
174 | if (color != null) { | |
175 | Position colorPos = new Position(pos.offset + index + "color:".length(), 1); | |
176 | ColorAnnotation colorAnnotation = support.findExistingAnnotation(colorPos); | |
177 | if (colorAnnotation == null) { | |
178 | colorAnnotation = new ColorAnnotation(colorPos, viewer); | |
179 | } | |
180 | colorAnnotation.setColor(color); | |
181 | annotations.add(colorAnnotation); | |
182 | } | |
183 | ||
184 | // rgb parameter names annotations | |
185 | int rgbIndex = line.indexOf("rgb"); | |
186 | if (rgbIndex != -1) { | |
187 | rgbIndex = rgbIndex + "rgb".length(); | |
188 | int startOffset = pos.offset + rgbIndex; | |
189 | String rgbContent = line.substring(rgbIndex, line.length()); | |
190 | int startIndex = addRGBParamNameAnnotation("red:", rgbContent, 0, startOffset, viewer, support, | |
191 | annotations); | |
192 | if (startIndex != -1) { | |
193 | startIndex = addRGBParamNameAnnotation("green:", rgbContent, startIndex, startOffset, viewer, | |
194 | support, annotations); | |
195 | if (startIndex != -1) { | |
196 | startIndex = addRGBParamNameAnnotation("blue:", rgbContent, startIndex, startOffset, | |
197 | viewer, support, annotations); | |
198 | } | |
199 | } | |
200 | } | |
201 | ||
202 | } catch (BadLocationException e) { | |
203 | e.printStackTrace(); | |
204 | } | |
205 | } | |
206 | } | |
207 | return annotations; | |
208 | } | |
209 | ||
210 | /** | |
211 | * Add RGB parameter name annotation | |
212 | * | |
213 | * @param paramName | |
214 | * @param rgbContent | |
215 | * @param startIndex | |
216 | * @param startOffset | |
217 | * @param viewer | |
218 | * @param support | |
219 | * @param annotations | |
220 | * @return the current parsed index | |
221 | */ | |
222 | private static int addRGBParamNameAnnotation(String paramName, String rgbContent, int startIndex, int startOffset, | |
223 | ISourceViewer viewer, InlinedAnnotationSupport support, Set<AbstractInlinedAnnotation> annotations) { | |
224 | char startChar = startIndex == 0 ? '(' : ','; | |
225 | char[] chars = rgbContent.toCharArray(); | |
226 | for (int i = startIndex; i < chars.length; i++) { | |
227 | char c = chars[i]; | |
228 | if (c == startChar) { | |
229 | if (i == chars.length - 1) { | |
230 | return -1; | |
231 | } | |
232 | Position paramPos = new Position(startOffset + i + 1, 1); | |
233 | LineContentAnnotation colorParamAnnotation = support.findExistingAnnotation(paramPos); | |
234 | if (colorParamAnnotation == null) { | |
235 | colorParamAnnotation = new LineContentAnnotation(paramPos, viewer); | |
236 | } | |
237 | colorParamAnnotation.setText(paramName); | |
238 | annotations.add(colorParamAnnotation); | |
239 | return i + 1; | |
240 | } | |
241 | } | |
242 | return -1; | |
243 | } | |
244 | ||
245 | /** | |
246 | * Parse the given input rgb color and returns an instance of SWT Color and null | |
247 | * otherwise. | |
248 | * | |
249 | * @param input | |
250 | * the rgb string color | |
251 | * @param device | |
252 | * @return the created color and null otherwise. | |
253 | */ | |
254 | private static Color parse(String input, Device device) { | |
255 | Pattern c = Pattern.compile("rgb *\\( *([0-9]+), *([0-9]+), *([0-9]+) *\\)"); | |
256 | Matcher m = c.matcher(input); | |
257 | if (m.matches()) { | |
258 | try { | |
259 | return new Color(device, Integer.valueOf(m.group(1)), // r | |
260 | Integer.valueOf(m.group(2)), // g | |
261 | Integer.valueOf(m.group(3))); // b | |
262 | } catch (Exception e) { | |
263 | ||
264 | } | |
265 | } | |
266 | return null; | |
267 | } | |
268 | ||
269 | /** | |
270 | * Returns the line text. | |
271 | * | |
272 | * @param document | |
273 | * the document. | |
274 | * @param line | |
275 | * the line index. | |
276 | * @return the line text. | |
277 | */ | |
278 | private static String getLineText(IDocument document, int line) { | |
279 | try { | |
280 | int offset = document.getLineOffset(line); | |
281 | int length = document.getLineLength(line); | |
282 | return document.get(offset, length); | |
283 | } catch (Exception e) { | |
284 | e.printStackTrace(); | |
285 | return null; | |
286 | } | |
287 | } | |
288 | } |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %Plugin.name |
3 | 3 | Bundle-SymbolicName: org.eclipse.jface.text.tests |
4 | Bundle-Version: 3.11.500.qualifier | |
4 | Bundle-Version: 3.11.700.qualifier | |
5 | 5 | Bundle-Vendor: %Plugin.providerName |
6 | 6 | Bundle-Localization: plugin |
7 | 7 | Export-Package: |
8 | 8 | org.eclipse.jface.text.tests, |
9 | org.eclipse.jface.text.tests.codemining, | |
9 | 10 | org.eclipse.jface.text.tests.contentassist, |
10 | 11 | org.eclipse.jface.text.tests.reconciler, |
11 | 12 | org.eclipse.jface.text.tests.rules, |
13 | 13 | <parent> |
14 | 14 | <artifactId>tests-pom</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | <relativePath>../tests-pom/</relativePath> |
18 | 18 | </parent> |
19 | 19 | <groupId>org.eclipse.jface</groupId> |
20 | 20 | <artifactId>org.eclipse.jface.text.tests</artifactId> |
21 | <version>3.11.500-SNAPSHOT</version> | |
21 | <version>3.11.700-SNAPSHOT</version> | |
22 | 22 | <packaging>eclipse-test-plugin</packaging> |
23 | 23 | <properties> |
24 | 24 | <testSuite>${project.artifactId}</testSuite> |
+1
-1
403 | 403 | fUndoManager.beginCompoundChange(); |
404 | 404 | fUndoManager.endCompoundChange(); |
405 | 405 | |
406 | // insert the atomic changes | |
406 | // insert the atomic changes | |
407 | 407 | doRepeatableChange(document); |
408 | 408 | |
409 | 409 | assertTrue(fUndoManager.undoable()); |
0 | /******************************************************************************* | |
1 | * Copyright (c) 2019 Red Hat Inc. and others. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * - Mickael Istria (Red Hat Inc.) - initial implementation | |
12 | *******************************************************************************/ | |
13 | package org.eclipse.jface.text.tests; | |
14 | ||
15 | import java.util.concurrent.atomic.AtomicInteger; | |
16 | ||
17 | import org.junit.After; | |
18 | import org.junit.Assert; | |
19 | import org.junit.Assume; | |
20 | import org.junit.Before; | |
21 | import org.junit.Rule; | |
22 | import org.junit.Test; | |
23 | ||
24 | import org.eclipse.swt.SWT; | |
25 | import org.eclipse.swt.custom.StyledText; | |
26 | import org.eclipse.swt.layout.FillLayout; | |
27 | import org.eclipse.swt.widgets.Display; | |
28 | import org.eclipse.swt.widgets.Shell; | |
29 | ||
30 | import org.eclipse.jface.util.Util; | |
31 | ||
32 | import org.eclipse.jface.text.BadLocationException; | |
33 | import org.eclipse.jface.text.Document; | |
34 | import org.eclipse.jface.text.IDocument; | |
35 | import org.eclipse.jface.text.IRegion; | |
36 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
37 | import org.eclipse.jface.text.reconciler.DirtyRegion; | |
38 | import org.eclipse.jface.text.reconciler.IReconcilingStrategy; | |
39 | import org.eclipse.jface.text.reconciler.MonoReconciler; | |
40 | import org.eclipse.jface.text.source.AnnotationModel; | |
41 | import org.eclipse.jface.text.source.AnnotationPainter; | |
42 | import org.eclipse.jface.text.source.SourceViewer; | |
43 | import org.eclipse.jface.text.tests.util.DisplayHelper; | |
44 | ||
45 | public class CodeMiningTest { | |
46 | ||
47 | private SourceViewer fViewer; | |
48 | private Shell fShell; | |
49 | ||
50 | @Rule public ScreenshotOnFailureRule rule = new ScreenshotOnFailureRule(); | |
51 | ||
52 | @Before | |
53 | public void setUp() { | |
54 | fShell= new Shell(Display.getDefault()); | |
55 | fShell.setSize(500, 200); | |
56 | fShell.setLayout(new FillLayout()); | |
57 | fViewer= new SourceViewer(fShell, null, SWT.NONE); | |
58 | final StyledText textWidget= fViewer.getTextWidget(); | |
59 | MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() { | |
60 | @Override | |
61 | public void setDocument(IDocument document) { | |
62 | fViewer.updateCodeMinings(); | |
63 | } | |
64 | ||
65 | @Override | |
66 | public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
67 | // nothing to do | |
68 | } | |
69 | ||
70 | @Override | |
71 | public void reconcile(IRegion partition) { | |
72 | fViewer.updateCodeMinings(); | |
73 | } | |
74 | }, false); | |
75 | reconciler.install(fViewer); | |
76 | fViewer.setDocument(new Document(), new AnnotationModel()); | |
77 | fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new DelayedEchoCodeMiningProvider() }); | |
78 | AnnotationPainter annotationPainter = new AnnotationPainter(fViewer, null); | |
79 | fViewer.setCodeMiningAnnotationPainter(annotationPainter); | |
80 | fViewer.addPainter(annotationPainter); | |
81 | // this currently needs to be repeated | |
82 | fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new DelayedEchoCodeMiningProvider() }); | |
83 | final Display display = textWidget.getDisplay(); | |
84 | fShell.open(); | |
85 | Assert.assertTrue(new DisplayHelper() { | |
86 | @Override | |
87 | protected boolean condition() { | |
88 | return fViewer.getTextWidget().isVisible(); | |
89 | } | |
90 | }.waitForCondition(display, 3000)); | |
91 | DisplayHelper.sleep(textWidget.getDisplay(), 1000); | |
92 | } | |
93 | ||
94 | @After | |
95 | public void tearDown() { | |
96 | fShell.dispose(); | |
97 | fViewer = null; | |
98 | } | |
99 | ||
100 | @Test | |
101 | public void testCodeMiningFirstLine() { | |
102 | fViewer.getDocument().set("echo"); | |
103 | Assert.assertTrue(new DisplayHelper() { | |
104 | @Override | |
105 | protected boolean condition() { | |
106 | return fViewer.getTextWidget().getLineVerticalIndent(0) > 0; | |
107 | } | |
108 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
109 | } | |
110 | ||
111 | @Test | |
112 | public void testCodeMiningCtrlHome() throws BadLocationException { | |
113 | Assume.assumeFalse("See bug 541415. For whatever reason, this shortcut doesn't work on Mac", Util.isMac()); | |
114 | DelayedEchoCodeMiningProvider.DELAY = 500; | |
115 | fViewer.getDocument().set(TextViewerTest.generate5000Lines()); | |
116 | Assert.assertTrue(new DisplayHelper() { | |
117 | @Override | |
118 | protected boolean condition() { | |
119 | return fViewer.getTextWidget().getText().length() > 5000; | |
120 | } | |
121 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
122 | TextViewerTest.ctrlEnd(fViewer); | |
123 | final int lastLine = fViewer.getDocument().getNumberOfLines() - 1; | |
124 | final int lastLineOffset = fViewer.getDocument().getLineOffset(lastLine); | |
125 | Assert.assertTrue(new DisplayHelper() { | |
126 | @Override | |
127 | protected boolean condition() { | |
128 | return lastLineOffset >= fViewer.getVisibleRegion().getOffset() && lastLineOffset <= fViewer.getVisibleRegion().getOffset() + fViewer.getVisibleRegion().getLength(); | |
129 | } | |
130 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
131 | DisplayHelper.sleep(fViewer.getControl().getDisplay(), 500); | |
132 | AtomicInteger events = new AtomicInteger(); | |
133 | fViewer.addViewportListener(offset -> | |
134 | events.incrementAndGet()); | |
135 | TextViewerTest.ctrlHome(fViewer); | |
136 | Assert.assertTrue(new DisplayHelper() { | |
137 | @Override | |
138 | protected boolean condition() { | |
139 | return events.get() > 0; | |
140 | } | |
141 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
142 | Assert.assertEquals(0, fViewer.getVisibleRegion().getOffset()); | |
143 | // wait for codemining to style line | |
144 | Assert.assertTrue(new DisplayHelper() { | |
145 | @Override | |
146 | protected boolean condition() { | |
147 | return fViewer.getTextWidget().getLineVerticalIndent(0) > 0; | |
148 | } | |
149 | }.waitForCondition(fViewer.getControl().getDisplay(), 300000)); | |
150 | } | |
151 | ||
152 | @Test | |
153 | public void testCodeMiningCtrlEnd() throws BadLocationException { | |
154 | Assume.assumeFalse("See bug 541415. For whatever reason, this shortcut doesn't work on Mac", Util.isMac()); | |
155 | fViewer.getDocument().set(TextViewerTest.generate5000Lines()); | |
156 | Assert.assertTrue(new DisplayHelper() { | |
157 | @Override | |
158 | protected boolean condition() { | |
159 | return fViewer.getTextWidget().getText().length() > 5000 && fViewer.getTextWidget().getLineVerticalIndent(0) > 0; | |
160 | } | |
161 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
162 | DisplayHelper.sleep(fViewer.getTextWidget().getDisplay(), 500); | |
163 | TextViewerTest.ctrlEnd(fViewer); | |
164 | final int lastLine = fViewer.getDocument().getNumberOfLines() - 1; | |
165 | final int lastLineOffset = fViewer.getDocument().getLineOffset(lastLine); | |
166 | Assert.assertTrue(new DisplayHelper() { | |
167 | @Override | |
168 | protected boolean condition() { | |
169 | return lastLineOffset >= fViewer.getVisibleRegion().getOffset() && lastLineOffset <= fViewer.getVisibleRegion().getOffset() + fViewer.getVisibleRegion().getLength(); | |
170 | } | |
171 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
172 | Assert.assertTrue(new DisplayHelper() { | |
173 | @Override | |
174 | protected boolean condition() { | |
175 | return fViewer.getTextWidget().getLineVerticalIndent(lastLine) > 0; | |
176 | } | |
177 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
178 | } | |
179 | ||
180 | } |
+0
-64
0 | /******************************************************************************* | |
1 | * Copyright (c) 2019 Red Hat Inc. and others. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * - Mickael Istria (Red Hat Inc.) - initial implementation | |
12 | *******************************************************************************/ | |
13 | package org.eclipse.jface.text.tests; | |
14 | ||
15 | import java.util.ArrayList; | |
16 | import java.util.List; | |
17 | import java.util.concurrent.CompletableFuture; | |
18 | ||
19 | import org.eclipse.core.runtime.IProgressMonitor; | |
20 | ||
21 | import org.eclipse.jface.text.BadLocationException; | |
22 | import org.eclipse.jface.text.IDocument; | |
23 | import org.eclipse.jface.text.ITextViewer; | |
24 | import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; | |
25 | import org.eclipse.jface.text.codemining.ICodeMining; | |
26 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
27 | import org.eclipse.jface.text.codemining.LineHeaderCodeMining; | |
28 | ||
29 | public class DelayedEchoCodeMiningProvider extends AbstractCodeMiningProvider implements ICodeMiningProvider { | |
30 | ||
31 | public static int DELAY = 0; | |
32 | ||
33 | @Override | |
34 | public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { | |
35 | return CompletableFuture.supplyAsync(() -> { | |
36 | try { | |
37 | Thread.sleep(DELAY); | |
38 | } catch (InterruptedException e1) { | |
39 | e1.printStackTrace(); | |
40 | return null; | |
41 | } | |
42 | IDocument document = viewer.getDocument(); | |
43 | List<ICodeMining> res = new ArrayList<>(); | |
44 | for (int lineNumber = 0; lineNumber < document.getNumberOfLines(); lineNumber++) { | |
45 | try { | |
46 | String lineContent = document.get(document.getLineOffset(lineNumber), document.getLineLength(lineNumber)); | |
47 | if (!lineContent.trim().isEmpty()) { | |
48 | LineHeaderCodeMining mining = new LineHeaderCodeMining(lineNumber, document, DelayedEchoCodeMiningProvider.this) { | |
49 | // Nothing in particular | |
50 | }; | |
51 | mining.setLabel(lineContent); | |
52 | res.add(mining); | |
53 | } | |
54 | } catch (BadLocationException e) { | |
55 | e.printStackTrace(); | |
56 | } | |
57 | ||
58 | } | |
59 | return res; | |
60 | }); | |
61 | } | |
62 | ||
63 | } |
+4
-1
16 | 16 | import org.junit.runners.Suite; |
17 | 17 | import org.junit.runners.Suite.SuiteClasses; |
18 | 18 | |
19 | import org.eclipse.jface.text.tests.codemining.CodeMiningTest; | |
20 | import org.eclipse.jface.text.tests.codemining.CodeMiningProjectionViewerTest; | |
19 | 21 | import org.eclipse.jface.text.tests.contentassist.AsyncContentAssistTest; |
20 | 22 | import org.eclipse.jface.text.tests.reconciler.AbstractReconcilerTest; |
21 | 23 | import org.eclipse.jface.text.tests.rules.DefaultPartitionerTest; |
54 | 56 | WordRuleTest.class, |
55 | 57 | |
56 | 58 | TemplatePersistenceDataTest.class, |
57 | CodeMiningTest.class | |
59 | CodeMiningTest.class, | |
60 | CodeMiningProjectionViewerTest.class | |
58 | 61 | }) |
59 | 62 | public class JFaceTextTestSuite { |
60 | 63 | // see @SuiteClasses |
+1
-1
16 | 16 | |
17 | 17 | import org.eclipse.test.Screenshots; |
18 | 18 | |
19 | final class ScreenshotOnFailureRule extends TestWatcher { | |
19 | public final class ScreenshotOnFailureRule extends TestWatcher { | |
20 | 20 | @Override |
21 | 21 | protected void failed(Throwable e, org.junit.runner.Description description) { |
22 | 22 | Screenshots.takeScreenshot(description.getTestClass(), description.getMethodName()); |
0 | 0 | /******************************************************************************* |
1 | * Copyright (c) 2014-2019 Google, Inc and others. | |
1 | * Copyright (c) 2014, 2019 Google, Inc and others. | |
2 | 2 | * |
3 | 3 | * This program and the accompanying materials |
4 | 4 | * are made available under the terms of the Eclipse Public License 2.0 |
8 | 8 | * SPDX-License-Identifier: EPL-2.0 |
9 | 9 | * |
10 | 10 | * Contributors: |
11 | * - Sergey Prigogin (Google) - initial API and implementation | |
12 | * - Mickael Istria (Red Hat Inc.) - [Bug 544708] Ctrl+Home | |
11 | * Sergey Prigogin (Google) - initial API and implementation | |
12 | * Mickael Istria (Red Hat Inc.) - [Bug 544708] Ctrl+Home | |
13 | * Paul Pazderski - [Bug 545530] Test for TextViewer's default IDocumentAdapter implementation. | |
13 | 14 | *******************************************************************************/ |
14 | 15 | package org.eclipse.jface.text.tests; |
15 | 16 | |
16 | ||
17 | 17 | import static org.junit.Assert.assertEquals; |
18 | 18 | import static org.junit.Assert.assertTrue; |
19 | import static org.junit.Assert.fail; | |
20 | import static org.junit.Assume.assumeNotNull; | |
19 | 21 | |
20 | 22 | import java.util.concurrent.atomic.AtomicBoolean; |
21 | 23 | |
24 | 26 | import org.junit.Test; |
25 | 27 | |
26 | 28 | import org.eclipse.swt.SWT; |
29 | import org.eclipse.swt.custom.StyledTextContent; | |
27 | 30 | import org.eclipse.swt.layout.FillLayout; |
28 | 31 | import org.eclipse.swt.widgets.Control; |
29 | 32 | import org.eclipse.swt.widgets.Display; |
34 | 37 | import org.eclipse.jface.util.Util; |
35 | 38 | |
36 | 39 | import org.eclipse.jface.text.Document; |
40 | import org.eclipse.jface.text.IDocumentAdapter; | |
37 | 41 | import org.eclipse.jface.text.ITextViewer; |
38 | 42 | import org.eclipse.jface.text.TextViewer; |
39 | 43 | import org.eclipse.jface.text.source.SourceViewer; |
123 | 127 | } |
124 | 128 | } |
125 | 129 | |
130 | /** | |
131 | * Test if {@link TextViewer}s default {@link IDocumentAdapter} implementation adhere to | |
132 | * {@link IDocumentAdapter}s JavaDoc. | |
133 | */ | |
134 | @Test | |
135 | public void testDefaultContentImplementation() { | |
136 | final Shell shell= new Shell(); | |
137 | try { | |
138 | final StyledTextContent content; | |
139 | try { | |
140 | final TextViewer textViewer= new TextViewer(shell, SWT.NONE); | |
141 | textViewer.setDocument(new Document()); | |
142 | content= textViewer.getTextWidget().getContent(); | |
143 | } catch (Exception ex) { | |
144 | fail("Failed to obtain default instance of TextViewers document adapter. " + ex.getMessage()); | |
145 | return; | |
146 | } | |
147 | assumeNotNull(content); | |
148 | ||
149 | final String line0= "Hello "; | |
150 | final String line1= ""; | |
151 | final String line2= "World!"; | |
152 | final String text= line0 + "\n" + line1 + "\r\n" + line2; | |
153 | content.setText(text); | |
154 | assertEquals("Get text range failed.", "H", content.getTextRange(0, 1)); | |
155 | assertEquals("Get text range failed.", "ll", content.getTextRange(2, 2)); | |
156 | assertEquals("Adapter content length wrong.", text.length(), content.getCharCount()); | |
157 | assertEquals("Adapter returned wrong content.", line0, content.getLine(0)); | |
158 | assertEquals("Adapter returned wrong content.", line1, content.getLine(1)); | |
159 | assertEquals("Adapter returned wrong content.", line2, content.getLine(2)); | |
160 | ||
161 | content.setText("\r\n\r\n"); | |
162 | assertEquals("Wrong line for offset.", 0, content.getLineAtOffset(0)); | |
163 | assertEquals("Wrong line for offset.", 0, content.getLineAtOffset(1)); | |
164 | assertEquals("Wrong line for offset.", 1, content.getLineAtOffset(2)); | |
165 | assertEquals("Wrong line for offset.", 1, content.getLineAtOffset(3)); | |
166 | assertEquals("Wrong line for offset.", 2, content.getLineAtOffset(4)); | |
167 | assertEquals("Wrong line for offset.", content.getLineCount() - 1, content.getLineAtOffset(content.getCharCount())); | |
168 | ||
169 | content.setText(null); | |
170 | assertEquals("Adapter returned wrong line count.", 1, content.getLineCount()); | |
171 | content.setText(""); | |
172 | assertEquals("Adapter returned wrong line count.", 1, content.getLineCount()); | |
173 | content.setText("a\n"); | |
174 | assertEquals("Adapter returned wrong line count.", 2, content.getLineCount()); | |
175 | content.setText("\n\n"); | |
176 | assertEquals("Adapter returned wrong line count.", 3, content.getLineCount()); | |
177 | ||
178 | content.setText("\r\ntest\r\n"); | |
179 | assertEquals("Wrong offset for line.", 0, content.getOffsetAtLine(0)); | |
180 | assertEquals("Wrong offset for line.", 2, content.getOffsetAtLine(1)); | |
181 | assertEquals("Wrong offset for line.", 8, content.getOffsetAtLine(2)); | |
182 | content.setText(""); | |
183 | assertEquals("Wrong offset for line.", 0, content.getOffsetAtLine(0)); | |
184 | } finally { | |
185 | shell.dispose(); | |
186 | } | |
187 | } | |
188 | ||
126 | 189 | public static void ctrlEnd(ITextViewer viewer) { |
127 | 190 | postKeyEvent(viewer.getTextWidget(), SWT.END, SWT.CTRL, SWT.KeyDown); |
128 | 191 | } |
149 | 212 | DisplayHelper.driveEventQueue(display); |
150 | 213 | } |
151 | 214 | |
152 | ||
153 | 215 | public static String generate5000Lines() { |
154 | 216 | StringBuilder b = new StringBuilder("start"); |
155 | 217 | for (int i = 0; i < 5000; i++) { |
158 | 220 | b.append("end"); |
159 | 221 | return b.toString(); |
160 | 222 | } |
161 | ||
162 | 223 | } |
+162
-0
0 | /** | |
1 | * Copyright (c) 2019 Red Hat Inc., and others | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * - Mickael Istria (Red Hat Inc.) | |
12 | */ | |
13 | package org.eclipse.jface.text.tests.codemining; | |
14 | ||
15 | import java.util.ArrayList; | |
16 | import java.util.List; | |
17 | import java.util.concurrent.CompletableFuture; | |
18 | import java.util.concurrent.atomic.AtomicReference; | |
19 | ||
20 | import org.junit.After; | |
21 | import org.junit.Assert; | |
22 | import org.junit.Before; | |
23 | import org.junit.Test; | |
24 | import org.osgi.framework.Bundle; | |
25 | ||
26 | import org.eclipse.swt.SWT; | |
27 | import org.eclipse.swt.graphics.Color; | |
28 | import org.eclipse.swt.graphics.RGB; | |
29 | import org.eclipse.swt.layout.FillLayout; | |
30 | import org.eclipse.swt.widgets.Shell; | |
31 | ||
32 | import org.eclipse.core.runtime.ILog; | |
33 | import org.eclipse.core.runtime.ILogListener; | |
34 | import org.eclipse.core.runtime.IProgressMonitor; | |
35 | import org.eclipse.core.runtime.IStatus; | |
36 | import org.eclipse.core.runtime.Platform; | |
37 | ||
38 | import org.eclipse.jface.text.BadLocationException; | |
39 | import org.eclipse.jface.text.Document; | |
40 | import org.eclipse.jface.text.ITextViewer; | |
41 | import org.eclipse.jface.text.Position; | |
42 | import org.eclipse.jface.text.codemining.ICodeMining; | |
43 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
44 | import org.eclipse.jface.text.codemining.LineContentCodeMining; | |
45 | import org.eclipse.jface.text.source.Annotation; | |
46 | import org.eclipse.jface.text.source.AnnotationPainter; | |
47 | import org.eclipse.jface.text.source.IAnnotationAccess; | |
48 | import org.eclipse.jface.text.source.ISharedTextColors; | |
49 | import org.eclipse.jface.text.source.projection.ProjectionAnnotation; | |
50 | import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; | |
51 | import org.eclipse.jface.text.source.projection.ProjectionSupport; | |
52 | import org.eclipse.jface.text.source.projection.ProjectionViewer; | |
53 | import org.eclipse.jface.text.tests.util.DisplayHelper; | |
54 | ||
55 | public class CodeMiningProjectionViewerTest { | |
56 | ||
57 | private final class RepeatLettersCodeMiningProvider implements ICodeMiningProvider { | |
58 | @Override | |
59 | public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { | |
60 | List<LineContentCodeMining> codeMinings = new ArrayList<>(); | |
61 | for (int i = 0; i < viewer.getDocument().getLength(); i++) { | |
62 | try { | |
63 | char c= viewer.getDocument().getChar(i); | |
64 | if (Character.isLetter(c)) { | |
65 | codeMinings.add(new StaticContentLineCodeMining(i, c, this)); | |
66 | } | |
67 | } catch (BadLocationException e) { | |
68 | e.printStackTrace(); | |
69 | } | |
70 | } | |
71 | return CompletableFuture.completedFuture(codeMinings); | |
72 | } | |
73 | ||
74 | @Override | |
75 | public void dispose() { | |
76 | } | |
77 | } | |
78 | ||
79 | private Shell fParent; | |
80 | private ProjectionViewer fViewer; | |
81 | ||
82 | @Before | |
83 | public void setUp() { | |
84 | fParent= new Shell(); | |
85 | fParent.setSize(500, 200); | |
86 | fParent.setLayout(new FillLayout()); | |
87 | fViewer= new ProjectionViewer(fParent, null, null, false, SWT.NONE); | |
88 | IAnnotationAccess annotationAccess = new IAnnotationAccess() { | |
89 | @Override | |
90 | public Object getType(Annotation annotation) { | |
91 | return annotation.getType(); | |
92 | } | |
93 | ||
94 | @Override | |
95 | public boolean isMultiLine(Annotation annotation) { | |
96 | return true; | |
97 | } | |
98 | ||
99 | @Override | |
100 | public boolean isTemporary(Annotation annotation) { | |
101 | return true; | |
102 | } | |
103 | }; | |
104 | // code minings | |
105 | AnnotationPainter painter = new AnnotationPainter(fViewer, annotationAccess); | |
106 | fViewer.addPainter(painter); | |
107 | fViewer.setCodeMiningAnnotationPainter(painter); | |
108 | // projection/folding | |
109 | fViewer.setDocument(new Document(), new ProjectionAnnotationModel()); | |
110 | ProjectionSupport projectionSupport = new ProjectionSupport(fViewer, annotationAccess, new ISharedTextColors() { | |
111 | @Override | |
112 | public Color getColor(RGB rgb) { | |
113 | return null; | |
114 | } | |
115 | ||
116 | @Override | |
117 | public void dispose() { | |
118 | } | |
119 | }); | |
120 | projectionSupport.install(); | |
121 | fViewer.doOperation(ProjectionViewer.TOGGLE); | |
122 | } | |
123 | ||
124 | @After | |
125 | public void tearDown() { | |
126 | fParent.dispose(); | |
127 | } | |
128 | ||
129 | @Test | |
130 | public void testCollapse() throws Exception { | |
131 | fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { | |
132 | new RepeatLettersCodeMiningProvider() | |
133 | }); | |
134 | fViewer.getDocument().set("1a\n2a\n3a\n4a\n5a\n6a\n"); | |
135 | ProjectionAnnotation annotation= new ProjectionAnnotation(true); | |
136 | fViewer.getProjectionAnnotationModel().addAnnotation(annotation, new Position(0, fViewer.getDocument().getLineOffset(4))); | |
137 | fViewer.doOperation(ProjectionViewer.COLLAPSE_ALL); | |
138 | fViewer.updateCodeMinings(); | |
139 | fParent.open(); | |
140 | ||
141 | Bundle bundle = Platform.getBundle("org.eclipse.ui.workbench"); | |
142 | ILog log = null; | |
143 | AtomicReference<IStatus> logError = new AtomicReference<>(); | |
144 | ILogListener logListener= (status, plugin) -> { | |
145 | logError.set(status); | |
146 | }; | |
147 | if (bundle != null && bundle.getState() == Bundle.ACTIVE) { | |
148 | log = Platform.getLog(bundle); | |
149 | log.addLogListener(logListener); | |
150 | } | |
151 | try { | |
152 | // without workbench, next line throws Exception directly | |
153 | DisplayHelper.sleep(fParent.getDisplay(), 1000); | |
154 | Assert.assertNull(logError.get()); | |
155 | } finally { | |
156 | if (log != null) { | |
157 | log.removeLogListener(logListener); | |
158 | } | |
159 | } | |
160 | } | |
161 | } |
+247
-0
0 | /******************************************************************************* | |
1 | * Copyright (c) 2019 Red Hat Inc. and others. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * - Mickael Istria (Red Hat Inc.) - initial implementation | |
12 | *******************************************************************************/ | |
13 | package org.eclipse.jface.text.tests.codemining; | |
14 | ||
15 | import java.util.Collections; | |
16 | import java.util.List; | |
17 | import java.util.concurrent.CompletableFuture; | |
18 | import java.util.concurrent.atomic.AtomicInteger; | |
19 | ||
20 | import org.junit.After; | |
21 | import org.junit.Assert; | |
22 | import org.junit.Assume; | |
23 | import org.junit.Before; | |
24 | import org.junit.Rule; | |
25 | import org.junit.Test; | |
26 | ||
27 | import org.eclipse.swt.SWT; | |
28 | import org.eclipse.swt.custom.StyledText; | |
29 | import org.eclipse.swt.graphics.GC; | |
30 | import org.eclipse.swt.graphics.Image; | |
31 | import org.eclipse.swt.graphics.ImageData; | |
32 | import org.eclipse.swt.graphics.Rectangle; | |
33 | import org.eclipse.swt.layout.FillLayout; | |
34 | import org.eclipse.swt.widgets.Display; | |
35 | import org.eclipse.swt.widgets.Shell; | |
36 | ||
37 | import org.eclipse.core.runtime.IProgressMonitor; | |
38 | ||
39 | import org.eclipse.jface.util.Util; | |
40 | ||
41 | import org.eclipse.jface.text.BadLocationException; | |
42 | import org.eclipse.jface.text.Document; | |
43 | import org.eclipse.jface.text.IDocument; | |
44 | import org.eclipse.jface.text.IRegion; | |
45 | import org.eclipse.jface.text.ITextViewer; | |
46 | import org.eclipse.jface.text.Position; | |
47 | import org.eclipse.jface.text.codemining.ICodeMining; | |
48 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
49 | import org.eclipse.jface.text.reconciler.DirtyRegion; | |
50 | import org.eclipse.jface.text.reconciler.IReconcilingStrategy; | |
51 | import org.eclipse.jface.text.reconciler.MonoReconciler; | |
52 | import org.eclipse.jface.text.source.AnnotationModel; | |
53 | import org.eclipse.jface.text.source.AnnotationPainter; | |
54 | import org.eclipse.jface.text.source.SourceViewer; | |
55 | import org.eclipse.jface.text.tests.ScreenshotOnFailureRule; | |
56 | import org.eclipse.jface.text.tests.TextViewerTest; | |
57 | import org.eclipse.jface.text.tests.util.DisplayHelper; | |
58 | ||
59 | public class CodeMiningTest { | |
60 | ||
61 | private SourceViewer fViewer; | |
62 | private Shell fShell; | |
63 | ||
64 | @Rule public ScreenshotOnFailureRule rule = new ScreenshotOnFailureRule(); | |
65 | ||
66 | @Before | |
67 | public void setUp() { | |
68 | fShell= new Shell(Display.getDefault()); | |
69 | fShell.setSize(500, 200); | |
70 | fShell.setLayout(new FillLayout()); | |
71 | fViewer= new SourceViewer(fShell, null, SWT.NONE); | |
72 | final StyledText textWidget= fViewer.getTextWidget(); | |
73 | textWidget.setText("a"); | |
74 | textWidget.setText(""); | |
75 | MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() { | |
76 | @Override | |
77 | public void setDocument(IDocument document) { | |
78 | fViewer.updateCodeMinings(); | |
79 | } | |
80 | ||
81 | @Override | |
82 | public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
83 | // nothing to do | |
84 | } | |
85 | ||
86 | @Override | |
87 | public void reconcile(IRegion partition) { | |
88 | fViewer.updateCodeMinings(); | |
89 | } | |
90 | }, false); | |
91 | reconciler.install(fViewer); | |
92 | fViewer.setDocument(new Document(), new AnnotationModel()); | |
93 | fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new DelayedEchoCodeMiningProvider() }); | |
94 | AnnotationPainter annotationPainter = new AnnotationPainter(fViewer, null); | |
95 | fViewer.setCodeMiningAnnotationPainter(annotationPainter); | |
96 | fViewer.addPainter(annotationPainter); | |
97 | // this currently needs to be repeated | |
98 | fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new DelayedEchoCodeMiningProvider() }); | |
99 | final Display display = textWidget.getDisplay(); | |
100 | fShell.open(); | |
101 | Assert.assertTrue(new DisplayHelper() { | |
102 | @Override | |
103 | protected boolean condition() { | |
104 | return fViewer.getTextWidget().isVisible(); | |
105 | } | |
106 | }.waitForCondition(display, 3000)); | |
107 | DisplayHelper.sleep(textWidget.getDisplay(), 1000); | |
108 | } | |
109 | ||
110 | @After | |
111 | public void tearDown() { | |
112 | fShell.dispose(); | |
113 | fViewer = null; | |
114 | } | |
115 | ||
116 | @Test | |
117 | public void testCodeMiningFirstLine() { | |
118 | fViewer.getDocument().set("echo"); | |
119 | Assert.assertTrue(new DisplayHelper() { | |
120 | @Override | |
121 | protected boolean condition() { | |
122 | return fViewer.getTextWidget().getLineVerticalIndent(0) > 0; | |
123 | } | |
124 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
125 | } | |
126 | ||
127 | @Test | |
128 | public void testCodeMiningCtrlHome() throws BadLocationException { | |
129 | Assume.assumeFalse("See bug 541415. For whatever reason, this shortcut doesn't work on Mac", Util.isMac()); | |
130 | DelayedEchoCodeMiningProvider.DELAY = 500; | |
131 | fViewer.getDocument().set(TextViewerTest.generate5000Lines()); | |
132 | Assert.assertTrue(new DisplayHelper() { | |
133 | @Override | |
134 | protected boolean condition() { | |
135 | return fViewer.getTextWidget().getText().length() > 5000; | |
136 | } | |
137 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
138 | TextViewerTest.ctrlEnd(fViewer); | |
139 | final int lastLine = fViewer.getDocument().getNumberOfLines() - 1; | |
140 | final int lastLineOffset = fViewer.getDocument().getLineOffset(lastLine); | |
141 | Assert.assertTrue(new DisplayHelper() { | |
142 | @Override | |
143 | protected boolean condition() { | |
144 | return lastLineOffset >= fViewer.getVisibleRegion().getOffset() && lastLineOffset <= fViewer.getVisibleRegion().getOffset() + fViewer.getVisibleRegion().getLength(); | |
145 | } | |
146 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
147 | DisplayHelper.sleep(fViewer.getControl().getDisplay(), 500); | |
148 | AtomicInteger events = new AtomicInteger(); | |
149 | fViewer.addViewportListener(offset -> | |
150 | events.incrementAndGet()); | |
151 | TextViewerTest.ctrlHome(fViewer); | |
152 | Assert.assertTrue(new DisplayHelper() { | |
153 | @Override | |
154 | protected boolean condition() { | |
155 | return events.get() > 0; | |
156 | } | |
157 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
158 | Assert.assertEquals(0, fViewer.getVisibleRegion().getOffset()); | |
159 | // wait for codemining to style line | |
160 | Assert.assertTrue(new DisplayHelper() { | |
161 | @Override | |
162 | protected boolean condition() { | |
163 | return fViewer.getTextWidget().getLineVerticalIndent(0) > 0; | |
164 | } | |
165 | }.waitForCondition(fViewer.getControl().getDisplay(), 300000)); | |
166 | } | |
167 | ||
168 | @Test | |
169 | public void testCodeMiningCtrlEnd() throws BadLocationException { | |
170 | Assume.assumeFalse("See bug 541415. For whatever reason, this shortcut doesn't work on Mac", Util.isMac()); | |
171 | fViewer.getDocument().set(TextViewerTest.generate5000Lines()); | |
172 | Assert.assertTrue(new DisplayHelper() { | |
173 | @Override | |
174 | protected boolean condition() { | |
175 | return fViewer.getTextWidget().getText().length() > 5000 && fViewer.getTextWidget().getLineVerticalIndent(0) > 0; | |
176 | } | |
177 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
178 | DisplayHelper.sleep(fViewer.getTextWidget().getDisplay(), 500); | |
179 | TextViewerTest.ctrlEnd(fViewer); | |
180 | final int lastLine = fViewer.getDocument().getNumberOfLines() - 1; | |
181 | final int lastLineOffset = fViewer.getDocument().getLineOffset(lastLine); | |
182 | Assert.assertTrue(new DisplayHelper() { | |
183 | @Override | |
184 | protected boolean condition() { | |
185 | return lastLineOffset >= fViewer.getVisibleRegion().getOffset() && lastLineOffset <= fViewer.getVisibleRegion().getOffset() + fViewer.getVisibleRegion().getLength(); | |
186 | } | |
187 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
188 | Assert.assertTrue(new DisplayHelper() { | |
189 | @Override | |
190 | protected boolean condition() { | |
191 | return fViewer.getTextWidget().getLineVerticalIndent(lastLine) > 0; | |
192 | } | |
193 | }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); | |
194 | } | |
195 | ||
196 | @Test | |
197 | public void testCodeMiningMultiLine() throws BadLocationException { | |
198 | fViewer.getDocument().set("a\nbc"); | |
199 | fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new ICodeMiningProvider() { | |
200 | @Override | |
201 | public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { | |
202 | return CompletableFuture.completedFuture(Collections.singletonList(new StaticContentLineCodeMining(new Position(0, 3), "long enough code mining to be wider than actual text", this))); | |
203 | } | |
204 | ||
205 | @Override | |
206 | public void dispose() { | |
207 | } | |
208 | } }); | |
209 | StyledText widget = fViewer.getTextWidget(); | |
210 | Assert.assertFalse("Code mining is visible on 2nd line", new DisplayHelper() { | |
211 | @Override | |
212 | protected boolean condition() { | |
213 | try { | |
214 | return widget.getStyleRangeAtOffset(0) != null && widget.getStyleRangeAtOffset(0).metrics != null | |
215 | && hasCodeMiningPrintedAfterTextOnLine(fViewer, 1); | |
216 | } catch (BadLocationException e) { | |
217 | e.printStackTrace(); | |
218 | return true; | |
219 | } | |
220 | } | |
221 | }.waitForCondition(fViewer.getTextWidget().getDisplay(), 1000)); | |
222 | } | |
223 | ||
224 | private static boolean hasCodeMiningPrintedAfterTextOnLine(ITextViewer viewer, int line) throws BadLocationException { | |
225 | StyledText widget = viewer.getTextWidget(); | |
226 | IDocument document= viewer.getDocument(); | |
227 | Rectangle secondLineBounds = widget.getTextBounds(document.getLineOffset(1), document.getLineOffset(line) + document.getLineLength(line) - 1); | |
228 | Image image = new Image(widget.getDisplay(), widget.getSize().x, widget.getSize().y); | |
229 | GC gc = new GC(widget); | |
230 | gc.copyArea(image, 0, 0); | |
231 | gc.dispose(); | |
232 | ImageData imageData = image.getImageData(); | |
233 | secondLineBounds.x += secondLineBounds.width; // look only area after text | |
234 | for (int x = secondLineBounds.x + 1; x < image.getBounds().width && x < imageData.width; x++) { | |
235 | for (int y = secondLineBounds.y; y < secondLineBounds.y + secondLineBounds.height && y < imageData.height; y++) { | |
236 | if (!imageData.palette.getRGB(imageData.getPixel(x, y)).equals(widget.getBackground().getRGB())) { | |
237 | // code mining printed | |
238 | image.dispose(); | |
239 | return true; | |
240 | } | |
241 | } | |
242 | } | |
243 | image.dispose(); | |
244 | return false; | |
245 | } | |
246 | } |
+63
-0
0 | /******************************************************************************* | |
1 | * Copyright (c) 2019 Red Hat Inc. and others. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * - Mickael Istria (Red Hat Inc.) - initial implementation | |
12 | *******************************************************************************/ | |
13 | package org.eclipse.jface.text.tests.codemining; | |
14 | ||
15 | import java.util.ArrayList; | |
16 | import java.util.List; | |
17 | import java.util.concurrent.CompletableFuture; | |
18 | ||
19 | import org.eclipse.core.runtime.IProgressMonitor; | |
20 | ||
21 | import org.eclipse.jface.text.BadLocationException; | |
22 | import org.eclipse.jface.text.IDocument; | |
23 | import org.eclipse.jface.text.ITextViewer; | |
24 | import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; | |
25 | import org.eclipse.jface.text.codemining.ICodeMining; | |
26 | import org.eclipse.jface.text.codemining.LineHeaderCodeMining; | |
27 | ||
28 | public class DelayedEchoCodeMiningProvider extends AbstractCodeMiningProvider { | |
29 | ||
30 | public static int DELAY = 0; | |
31 | ||
32 | @Override | |
33 | public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { | |
34 | return CompletableFuture.supplyAsync(() -> { | |
35 | try { | |
36 | Thread.sleep(DELAY); | |
37 | } catch (InterruptedException e1) { | |
38 | e1.printStackTrace(); | |
39 | return null; | |
40 | } | |
41 | IDocument document = viewer.getDocument(); | |
42 | List<ICodeMining> res = new ArrayList<>(); | |
43 | for (int lineNumber = 0; lineNumber < document.getNumberOfLines(); lineNumber++) { | |
44 | try { | |
45 | String lineContent = document.get(document.getLineOffset(lineNumber), document.getLineLength(lineNumber)); | |
46 | if (!lineContent.trim().isEmpty()) { | |
47 | LineHeaderCodeMining mining = new LineHeaderCodeMining(lineNumber, document, DelayedEchoCodeMiningProvider.this) { | |
48 | // Nothing in particular | |
49 | }; | |
50 | mining.setLabel(lineContent); | |
51 | res.add(mining); | |
52 | } | |
53 | } catch (BadLocationException e) { | |
54 | e.printStackTrace(); | |
55 | } | |
56 | ||
57 | } | |
58 | return res; | |
59 | }); | |
60 | } | |
61 | ||
62 | } |
+37
-0
0 | /** | |
1 | * Copyright (c) 2019 Red Hat Inc., and others | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * - Mickael Istria (Red Hat Inc.) | |
12 | */ | |
13 | package org.eclipse.jface.text.tests.codemining; | |
14 | ||
15 | import org.eclipse.jface.text.Position; | |
16 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
17 | import org.eclipse.jface.text.codemining.LineContentCodeMining; | |
18 | ||
19 | public class StaticContentLineCodeMining extends LineContentCodeMining { | |
20 | ||
21 | public StaticContentLineCodeMining(Position position, String message, ICodeMiningProvider provider) { | |
22 | super(position, provider); | |
23 | setLabel(message); | |
24 | } | |
25 | ||
26 | public StaticContentLineCodeMining(int i, char c, ICodeMiningProvider repeatLettersCodeMiningProvider) { | |
27 | super(new Position(i, 1), repeatLettersCodeMiningProvider); | |
28 | setLabel(Character.toString(c)); | |
29 | } | |
30 | ||
31 | @Override | |
32 | public boolean isResolved() { | |
33 | return true; | |
34 | } | |
35 | ||
36 | } |
+1
-1
242 | 242 | int[] offsets= new int[] { 0, 1 }; |
243 | 243 | assertComputePartitioning_InterleavingPartitions(offsets); |
244 | 244 | |
245 | } | |
245 | } | |
246 | 246 | |
247 | 247 | @Test |
248 | 248 | public void testBug368219_1() throws Exception { |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %pluginName |
3 | 3 | Bundle-SymbolicName: org.eclipse.search; singleton:=true |
4 | Bundle-Version: 3.11.500.qualifier | |
4 | Bundle-Version: 3.11.600.qualifier | |
5 | 5 | Bundle-Activator: org.eclipse.search.internal.ui.SearchPlugin |
6 | 6 | Bundle-ActivationPolicy: lazy |
7 | 7 | Bundle-Vendor: %providerName |
97 | 97 | * The scope is WORKSPACE_SCOPE, SELECTED_PROJECTS_SCOPE, SELECTION_SCOPE or WORKING_SET_SCOPE. |
98 | 98 | * @param scope the newly selected scope |
99 | 99 | * |
100 | * @since 2.0 | |
100 | * @since 2.0 | |
101 | 101 | */ |
102 | 102 | public void setSelectedScope(int scope); |
103 | 103 | |
105 | 105 | * Tells whether a valid scope is selected. |
106 | 106 | * |
107 | 107 | * @return a <code>true</code> if a valid scope is selected in this search page container |
108 | * @since 2.0 | |
108 | * @since 2.0 | |
109 | 109 | */ |
110 | 110 | public boolean hasValidScope(); |
111 | 111 |
+16
-16
151 | 151 | } else { |
152 | 152 | fIsUIUpdateScheduled= false; |
153 | 153 | turnOnDecoration(); |
154 | updateBusyLabel(); | |
155 | if (fScheduleEnsureSelection) { | |
156 | fScheduleEnsureSelection= false; | |
157 | AbstractTextSearchResult result = getInput(); | |
158 | if (result != null && fViewer.getSelection().isEmpty()) { | |
159 | navigateNext(true); | |
160 | } | |
161 | } | |
154 | updateBusyLabel(); | |
155 | if (fScheduleEnsureSelection) { | |
156 | fScheduleEnsureSelection= false; | |
157 | AbstractTextSearchResult result = getInput(); | |
158 | if (result != null && fViewer.getSelection().isEmpty()) { | |
159 | navigateNext(true); | |
160 | } | |
161 | } | |
162 | 162 | } |
163 | 163 | fViewPart.updateLabel(); |
164 | 164 | return Status.OK_STATUS; |
209 | 209 | } |
210 | 210 | |
211 | 211 | private volatile boolean fIsUIUpdateScheduled= false; |
212 | private volatile boolean fScheduleEnsureSelection= false; | |
212 | private volatile boolean fScheduleEnsureSelection= false; | |
213 | 213 | private static final String KEY_LAYOUT = "org.eclipse.search.resultpage.layout"; //$NON-NLS-1$ |
214 | 214 | |
215 | 215 | /** |
605 | 605 | |
606 | 606 | @Override |
607 | 607 | public void queryFinished(final ISearchQuery query) { |
608 | // handle the end of the query in the UIUpdateJob, as ui updates | |
609 | // may not be finished here. | |
610 | postEnsureSelection(); | |
608 | // handle the end of the query in the UIUpdateJob, as ui updates | |
609 | // may not be finished here. | |
610 | postEnsureSelection(); | |
611 | 611 | } |
612 | 612 | }; |
613 | 613 | } |
616 | 616 | * Posts a UI update to make sure an element is selected. |
617 | 617 | * @since 3.2 |
618 | 618 | */ |
619 | protected void postEnsureSelection() { | |
620 | fScheduleEnsureSelection= true; | |
621 | scheduleUIUpdate(); | |
622 | } | |
619 | protected void postEnsureSelection() { | |
620 | fScheduleEnsureSelection= true; | |
621 | scheduleUIUpdate(); | |
622 | } | |
623 | 623 | |
624 | 624 | |
625 | 625 | private void updateBusyLabel() { |
20 | 20 | */ |
21 | 21 | public abstract class MatchFilter { |
22 | 22 | |
23 | /** | |
24 | * Returns whether the given match is filtered by this filter. | |
25 | * | |
26 | * @param match the match to look at | |
27 | * @return returns <code>true</code> if the given match should be filtered or <code>false</code> if not. | |
28 | */ | |
23 | /** | |
24 | * Returns whether the given match is filtered by this filter. | |
25 | * | |
26 | * @param match the match to look at | |
27 | * @return returns <code>true</code> if the given match should be filtered or <code>false</code> if not. | |
28 | */ | |
29 | 29 | public abstract boolean filters(Match match); |
30 | 30 | |
31 | /** | |
32 | * Returns the name of the filter as shown in the match filter selection dialog. | |
33 | * | |
34 | * @return the name of the filter as shown in the match filter selection dialog. | |
35 | */ | |
31 | /** | |
32 | * Returns the name of the filter as shown in the match filter selection dialog. | |
33 | * | |
34 | * @return the name of the filter as shown in the match filter selection dialog. | |
35 | */ | |
36 | 36 | public abstract String getName(); |
37 | 37 | |
38 | /** | |
39 | * Returns the description of the filter as shown in the match filter selection dialog. | |
40 | * | |
41 | * @return the description of the filter as shown in the match filter selection dialog. | |
42 | */ | |
38 | /** | |
39 | * Returns the description of the filter as shown in the match filter selection dialog. | |
40 | * | |
41 | * @return the description of the filter as shown in the match filter selection dialog. | |
42 | */ | |
43 | 43 | public abstract String getDescription(); |
44 | 44 | |
45 | 45 | /** |
46 | * Returns the label of the filter as shown by the filter action. | |
47 | * | |
48 | * @return the label of the filter as shown by the filter action. | |
49 | */ | |
46 | * Returns the label of the filter as shown by the filter action. | |
47 | * | |
48 | * @return the label of the filter as shown by the filter action. | |
49 | */ | |
50 | 50 | public abstract String getActionLabel(); |
51 | 51 | |
52 | 52 | /** |
53 | * Returns an ID of this filter. | |
54 | * | |
55 | * @return the id of the filter to be used when persisting this filter. | |
56 | */ | |
53 | * Returns an ID of this filter. | |
54 | * | |
55 | * @return the id of the filter to be used when persisting this filter. | |
56 | */ | |
57 | 57 | public abstract String getID(); |
58 | 58 | |
59 | 59 | } |
+3
-3
36 | 36 | |
37 | 37 | /** |
38 | 38 | * Specified the input for a search query. |
39 | * <p> | |
40 | * Clients may instantiate this class. | |
41 | * </p> | |
39 | * <p> | |
40 | * Clients may instantiate this class. | |
41 | * </p> | |
42 | 42 | */ |
43 | 43 | public static abstract class TextSearchInput { |
44 | 44 |
+1
-1
35 | 35 | private ISearchResult fSearch; |
36 | 36 | |
37 | 37 | public ShowSearchFromHistoryAction(ISearchResult search) { |
38 | super("", AS_RADIO_BUTTON); //$NON-NLS-1$ | |
38 | super("", AS_RADIO_BUTTON); //$NON-NLS-1$ | |
39 | 39 | fSearch= search; |
40 | 40 | |
41 | 41 | String label= escapeAmp(search.getLabel()); |
+7
-7
320 | 320 | table.setLayoutData(gd); |
321 | 321 | |
322 | 322 | |
323 | fRemoveButton= new Button(parent, SWT.PUSH); | |
324 | fRemoveButton.setText(SearchMessages.SearchesDialog_remove_label); | |
325 | fRemoveButton.addSelectionListener(new SelectionAdapter() { | |
326 | @Override | |
323 | fRemoveButton= new Button(parent, SWT.PUSH); | |
324 | fRemoveButton.setText(SearchMessages.SearchesDialog_remove_label); | |
325 | fRemoveButton.addSelectionListener(new SelectionAdapter() { | |
326 | @Override | |
327 | 327 | public void widgetSelected(SelectionEvent event) { |
328 | buttonPressed(REMOVE_ID); | |
329 | } | |
330 | }); | |
328 | buttonPressed(REMOVE_ID); | |
329 | } | |
330 | }); | |
331 | 331 | fRemoveButton.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING, false, false)); |
332 | 332 | SWTUtil.setButtonDimensionHint(fRemoveButton); |
333 | 333 |
22 | 22 | // Do not instantiate |
23 | 23 | } |
24 | 24 | |
25 | static { | |
26 | NLS.initializeMessages(BUNDLE_NAME, SearchMessages.class); | |
27 | } | |
25 | static { | |
26 | NLS.initializeMessages(BUNDLE_NAME, SearchMessages.class); | |
27 | } | |
28 | 28 | |
29 | 29 | public static String AbstractTextSearchViewPage_update_job_name; |
30 | 30 | public static String MatchFilterSelectionAction_label; |
91 | 91 | public static String PinSearchViewAction_label; |
92 | 92 | public static String PinSearchViewAction_tooltip; |
93 | 93 | public static String SearchPageRegistry_error_creating_extensionpoint; |
94 | public static String TextSearchGroup_submenu_text; | |
95 | public static String FindInWorkspaceActionDelegate_text; | |
96 | public static String FindInProjectActionDelegate_text; | |
97 | public static String FindInWorkingSetActionDelegate_text; | |
98 | public static String FindInFileActionDelegate_text; | |
94 | public static String TextSearchGroup_submenu_text; | |
95 | public static String FindInWorkspaceActionDelegate_text; | |
96 | public static String FindInProjectActionDelegate_text; | |
97 | public static String FindInWorkingSetActionDelegate_text; | |
98 | public static String FindInFileActionDelegate_text; | |
99 | 99 | public static String TextSearchQueryProviderRegistry_defaultProviderLabel; |
100 | 100 | public static String RetrieverAction_dialog_title; |
101 | 101 | public static String RetrieverAction_empty_selection; |
13 | 13 | <parent> |
14 | 14 | <artifactId>eclipse.platform.text</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | </parent> |
18 | 18 | <groupId>org.eclipse.search</groupId> |
19 | 19 | <artifactId>org.eclipse.search</artifactId> |
20 | <version>3.11.500-SNAPSHOT</version> | |
20 | <version>3.11.600-SNAPSHOT</version> | |
21 | 21 | <packaging>eclipse-plugin</packaging> |
22 | 22 | </project> |
+1
-1
135 | 135 | * @return returns true if the file name is matching to a file name pattern |
136 | 136 | */ |
137 | 137 | private boolean matchesFileName(String fileName) { |
138 | return getFileNameMatcher().reset(fileName).matches(); | |
138 | return getFileNameMatcher().reset(fileName).matches(); | |
139 | 139 | } |
140 | 140 | |
141 | 141 | /** |
+74
-74
155 | 155 | } |
156 | 156 | |
157 | 157 | |
158 | private static boolean isWordChar(char c) { | |
159 | return Character.isLetterOrDigit(c); | |
160 | } | |
158 | private static boolean isWordChar(char c) { | |
159 | return Character.isLetterOrDigit(c); | |
160 | } | |
161 | 161 | |
162 | 162 | /** |
163 | 163 | * Creates a pattern element from an array of patterns in the old 'StringMatcher' format. |
171 | 171 | StringBuilder pattern= new StringBuilder(); |
172 | 172 | for (int i= 0; i < patterns.length; i++) { |
173 | 173 | if (i > 0) { |
174 | // note that this works only as we know that the operands of the | |
175 | // or expression will be simple and need no brackets. | |
174 | // note that this works only as we know that the operands of the | |
175 | // or expression will be simple and need no brackets. | |
176 | 176 | pattern.append('|'); |
177 | 177 | } |
178 | 178 | appendAsRegEx(true, patterns[i], pattern); |
182 | 182 | |
183 | 183 | |
184 | 184 | public static StringBuilder appendAsRegEx(boolean isStringMatcher, String pattern, StringBuilder buffer) { |
185 | boolean isEscaped= false; | |
186 | for (int i = 0; i < pattern.length(); i++) { | |
187 | char c = pattern.charAt(i); | |
188 | switch(c) { | |
189 | // the backslash | |
190 | case '\\': | |
191 | // the backslash is escape char in string matcher | |
192 | if (isStringMatcher && !isEscaped) { | |
193 | isEscaped= true; | |
194 | } | |
195 | else { | |
196 | buffer.append("\\\\"); //$NON-NLS-1$ | |
197 | isEscaped= false; | |
198 | } | |
199 | break; | |
200 | // characters that need to be escaped in the regex. | |
201 | case '(': | |
202 | case ')': | |
203 | case '{': | |
204 | case '}': | |
205 | case '.': | |
206 | case '[': | |
207 | case ']': | |
208 | case '$': | |
209 | case '^': | |
210 | case '+': | |
211 | case '|': | |
212 | if (isEscaped) { | |
213 | buffer.append("\\\\"); //$NON-NLS-1$ | |
214 | isEscaped= false; | |
215 | } | |
216 | buffer.append('\\'); | |
217 | buffer.append(c); | |
218 | break; | |
219 | case '?': | |
220 | if (isStringMatcher && !isEscaped) { | |
221 | buffer.append('.'); | |
222 | } | |
223 | else { | |
224 | buffer.append('\\'); | |
225 | buffer.append(c); | |
226 | isEscaped= false; | |
227 | } | |
228 | break; | |
229 | case '*': | |
230 | if (isStringMatcher && !isEscaped) { | |
231 | buffer.append(".*"); //$NON-NLS-1$ | |
232 | } | |
233 | else { | |
234 | buffer.append('\\'); | |
235 | buffer.append(c); | |
236 | isEscaped= false; | |
237 | } | |
238 | break; | |
239 | default: | |
240 | if (isEscaped) { | |
241 | buffer.append("\\\\"); //$NON-NLS-1$ | |
242 | isEscaped= false; | |
243 | } | |
244 | buffer.append(c); | |
245 | break; | |
246 | } | |
247 | } | |
248 | if (isEscaped) { | |
249 | buffer.append("\\\\"); //$NON-NLS-1$ | |
250 | isEscaped= false; | |
251 | } | |
252 | return buffer; | |
253 | } | |
185 | boolean isEscaped= false; | |
186 | for (int i = 0; i < pattern.length(); i++) { | |
187 | char c = pattern.charAt(i); | |
188 | switch(c) { | |
189 | // the backslash | |
190 | case '\\': | |
191 | // the backslash is escape char in string matcher | |
192 | if (isStringMatcher && !isEscaped) { | |
193 | isEscaped= true; | |
194 | } | |
195 | else { | |
196 | buffer.append("\\\\"); //$NON-NLS-1$ | |
197 | isEscaped= false; | |
198 | } | |
199 | break; | |
200 | // characters that need to be escaped in the regex. | |
201 | case '(': | |
202 | case ')': | |
203 | case '{': | |
204 | case '}': | |
205 | case '.': | |
206 | case '[': | |
207 | case ']': | |
208 | case '$': | |
209 | case '^': | |
210 | case '+': | |
211 | case '|': | |
212 | if (isEscaped) { | |
213 | buffer.append("\\\\"); //$NON-NLS-1$ | |
214 | isEscaped= false; | |
215 | } | |
216 | buffer.append('\\'); | |
217 | buffer.append(c); | |
218 | break; | |
219 | case '?': | |
220 | if (isStringMatcher && !isEscaped) { | |
221 | buffer.append('.'); | |
222 | } | |
223 | else { | |
224 | buffer.append('\\'); | |
225 | buffer.append(c); | |
226 | isEscaped= false; | |
227 | } | |
228 | break; | |
229 | case '*': | |
230 | if (isStringMatcher && !isEscaped) { | |
231 | buffer.append(".*"); //$NON-NLS-1$ | |
232 | } | |
233 | else { | |
234 | buffer.append('\\'); | |
235 | buffer.append(c); | |
236 | isEscaped= false; | |
237 | } | |
238 | break; | |
239 | default: | |
240 | if (isEscaped) { | |
241 | buffer.append("\\\\"); //$NON-NLS-1$ | |
242 | isEscaped= false; | |
243 | } | |
244 | buffer.append(c); | |
245 | break; | |
246 | } | |
247 | } | |
248 | if (isEscaped) { | |
249 | buffer.append("\\\\"); //$NON-NLS-1$ | |
250 | isEscaped= false; | |
251 | } | |
252 | return buffer; | |
253 | } | |
254 | 254 | |
255 | 255 | /** |
256 | 256 | * Interprets escaped characters in the given replace pattern. |
+1
-1
433 | 433 | System.out.println(Messages.format( |
434 | 434 | "[TextSearch] Search duration for {0} files in {1} jobs using {2} threads: {3}ms", args)); //$NON-NLS-1$ |
435 | 435 | } |
436 | } | |
436 | } | |
437 | 437 | } |
438 | 438 | |
439 | 439 | public IStatus search(TextSearchScope scope, IProgressMonitor monitor) { |
384 | 384 | ArrayList<SearchPageDescriptor> filteredList= new ArrayList<>(input.size()); |
385 | 385 | for (SearchPageDescriptor descriptor : input) { |
386 | 386 | if (!WorkbenchActivityHelper.filterItem(descriptor)) |
387 | filteredList.add(descriptor); | |
387 | filteredList.add(descriptor); | |
388 | 388 | |
389 | 389 | } |
390 | 390 | return filteredList; |
430 | 430 | for (int i= 0; i < numPages; i++) { |
431 | 431 | SearchPageDescriptor descriptor= getDescriptorAt(i); |
432 | 432 | if (WorkbenchActivityHelper.filterItem(descriptor)) |
433 | continue; | |
433 | continue; | |
434 | 434 | |
435 | 435 | final CTabItem item = new CTabItem(folder, SWT.NONE); |
436 | 436 | item.setData("descriptor", descriptor); //$NON-NLS-1$ |
479 | 479 | composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
480 | 480 | |
481 | 481 | // create help control if needed |
482 | if (isHelpAvailable()) { | |
483 | createHelpControl(composite); | |
484 | } | |
482 | if (isHelpAvailable()) { | |
483 | createHelpControl(composite); | |
484 | } | |
485 | 485 | fCustomizeButton= createButton(composite, CUSTOMIZE_ID, SearchMessages.SearchDialog_customize, true); |
486 | 486 | |
487 | 487 | Label filler= new Label(composite, SWT.NONE); |
215 | 215 | } |
216 | 216 | |
217 | 217 | // public static String ReplaceDialog2_nomatches_error; |
218 | public static String SearchPreferencePage_textSearchEngine; | |
218 | public static String SearchPreferencePage_textSearchEngine; | |
219 | 219 | public static String TextSearchEngineRegistry_defaulttextsearch_label; |
220 | 220 | public static String FileSearchQuery_singularPatternWithFileExt; |
221 | 221 | public static String FileSearchQuery_pluralPatternWithFileExt; |
+14
-14
191 | 191 | if (sizeHint != null) { |
192 | 192 | int commaSep= sizeHint.indexOf(','); |
193 | 193 | if (commaSep != -1) { |
194 | try { | |
195 | int xval= Integer.parseInt(sizeHint.substring(0, commaSep).trim()); | |
196 | int yval= Integer.parseInt(sizeHint.substring(commaSep + 1).trim()); | |
197 | return new Point(xval, yval); | |
198 | } catch (NumberFormatException e) { | |
199 | } | |
194 | try { | |
195 | int xval= Integer.parseInt(sizeHint.substring(0, commaSep).trim()); | |
196 | int yval= Integer.parseInt(sizeHint.substring(commaSep + 1).trim()); | |
197 | return new Point(xval, yval); | |
198 | } catch (NumberFormatException e) { | |
199 | } | |
200 | 200 | } |
201 | 201 | } |
202 | return UNKNOWN_SIZE; | |
202 | return UNKNOWN_SIZE; | |
203 | 203 | } |
204 | 204 | |
205 | 205 | /** |
366 | 366 | } |
367 | 367 | } |
368 | 368 | |
369 | @Override | |
369 | @Override | |
370 | 370 | public String getLocalId() { |
371 | return getId(); | |
372 | } | |
373 | ||
374 | @Override | |
371 | return getId(); | |
372 | } | |
373 | ||
374 | @Override | |
375 | 375 | public String getPluginId() { |
376 | return fElement.getContributor().getName(); | |
377 | } | |
376 | return fElement.getContributor().getName(); | |
377 | } | |
378 | 378 | } |
301 | 301 | } |
302 | 302 | |
303 | 303 | |
304 | public TextSearchEngineRegistry getTextSearchEngineRegistry() { | |
305 | if (fTextSearchEngineRegistry == null) { | |
306 | fTextSearchEngineRegistry= new TextSearchEngineRegistry(); | |
307 | } | |
308 | return fTextSearchEngineRegistry; | |
309 | } | |
310 | ||
311 | public TextSearchQueryProviderRegistry getTextSearchQueryProviderRegistry() { | |
312 | if (fTextSearchQueryProviderRegistry == null) { | |
313 | fTextSearchQueryProviderRegistry= new TextSearchQueryProviderRegistry(); | |
314 | } | |
315 | return fTextSearchQueryProviderRegistry; | |
316 | } | |
304 | public TextSearchEngineRegistry getTextSearchEngineRegistry() { | |
305 | if (fTextSearchEngineRegistry == null) { | |
306 | fTextSearchEngineRegistry= new TextSearchEngineRegistry(); | |
307 | } | |
308 | return fTextSearchEngineRegistry; | |
309 | } | |
310 | ||
311 | public TextSearchQueryProviderRegistry getTextSearchQueryProviderRegistry() { | |
312 | if (fTextSearchQueryProviderRegistry == null) { | |
313 | fTextSearchQueryProviderRegistry= new TextSearchQueryProviderRegistry(); | |
314 | } | |
315 | return fTextSearchQueryProviderRegistry; | |
316 | } | |
317 | 317 | |
318 | 318 | /** |
319 | 319 | * Creates all necessary sorter description nodes. |
+14
-14
52 | 52 | public static final String DEFAULT_PERSPECTIVE= "org.eclipse.search.defaultPerspective"; //$NON-NLS-1$ |
53 | 53 | private static final String NO_DEFAULT_PERSPECTIVE= "org.eclipse.search.defaultPerspective.none"; //$NON-NLS-1$ |
54 | 54 | public static final String BRING_VIEW_TO_FRONT= "org.eclipse.search.bringToFront"; //$NON-NLS-1$ |
55 | public static final String TEXT_SEARCH_ENGINE = "org.eclipse.search.textSearchEngine"; //$NON-NLS-1$ | |
56 | public static final String TEXT_SEARCH_QUERY_PROVIDER = "org.eclipse.search.textSearchQueryProvider"; //$NON-NLS-1$ | |
55 | public static final String TEXT_SEARCH_ENGINE = "org.eclipse.search.textSearchEngine"; //$NON-NLS-1$ | |
56 | public static final String TEXT_SEARCH_QUERY_PROVIDER = "org.eclipse.search.textSearchQueryProvider"; //$NON-NLS-1$ | |
57 | 57 | public static final String LIMIT_HISTORY= "org.eclipse.search.limitHistory"; //$NON-NLS-1$ |
58 | 58 | |
59 | 59 | private ColorFieldEditor fColorEditor; |
118 | 118 | POTENTIAL_MATCH_FG_COLOR, |
119 | 119 | SearchMessages.SearchPreferencePage_potentialMatchFgColor, |
120 | 120 | getFieldEditorParent() |
121 | ); | |
121 | ); | |
122 | 122 | addField(fColorEditor); |
123 | 123 | |
124 | 124 | fEmphasizedCheckbox.setEnabled(!arePotentialMatchesIgnored(), getFieldEditorParent()); |
133 | 133 | getFieldEditorParent()); |
134 | 134 | addField(comboEditor); |
135 | 135 | |
136 | // in case we have a contributed engine, let the user choose. | |
137 | TextSearchEngineRegistry reg= SearchPlugin.getDefault().getTextSearchEngineRegistry(); | |
138 | String[][] engineNamesAndIds= reg.getAvailableEngines(); | |
139 | if (engineNamesAndIds.length > 1) { | |
140 | comboEditor= new ComboFieldEditor( | |
141 | TEXT_SEARCH_ENGINE, | |
142 | SearchMessages.SearchPreferencePage_textSearchEngine, | |
143 | engineNamesAndIds, | |
144 | getFieldEditorParent()); | |
145 | addField(comboEditor); | |
146 | } | |
136 | // in case we have a contributed engine, let the user choose. | |
137 | TextSearchEngineRegistry reg= SearchPlugin.getDefault().getTextSearchEngineRegistry(); | |
138 | String[][] engineNamesAndIds= reg.getAvailableEngines(); | |
139 | if (engineNamesAndIds.length > 1) { | |
140 | comboEditor= new ComboFieldEditor( | |
141 | TEXT_SEARCH_ENGINE, | |
142 | SearchMessages.SearchPreferencePage_textSearchEngine, | |
143 | engineNamesAndIds, | |
144 | getFieldEditorParent()); | |
145 | addField(comboEditor); | |
146 | } | |
147 | 147 | } |
148 | 148 | |
149 | 149 | @Override |
131 | 131 | item.fill(parent, -1); |
132 | 132 | } |
133 | 133 | |
134 | @Override | |
134 | @Override | |
135 | 135 | public void run() { |
136 | 136 | // nothing to do |
137 | } | |
137 | } | |
138 | 138 | |
139 | 139 | private SorterDescriptor findSorter(String pageId) { |
140 | 140 | Iterator<SorterDescriptor> iter= SearchPlugin.getDefault().getSorterDescriptors().iterator(); |
89 | 89 | return 2; |
90 | 90 | } |
91 | 91 | |
92 | @Override | |
92 | @Override | |
93 | 93 | public int compare(Viewer viewer, Object e1, Object e2) { |
94 | int cat1 = category(e1); | |
95 | int cat2 = category(e2); | |
96 | ||
97 | if (cat1 != cat2) { | |
94 | int cat1 = category(e1); | |
95 | int cat2 = category(e2); | |
96 | ||
97 | if (cat1 != cat2) { | |
98 | 98 | return cat1 - cat2; |
99 | 99 | } |
100 | 100 | |
101 | if (e1 instanceof LineElement && e2 instanceof LineElement) { | |
101 | if (e1 instanceof LineElement && e2 instanceof LineElement) { | |
102 | 102 | LineElement m1= (LineElement) e1; |
103 | 103 | LineElement m2= (LineElement) e2; |
104 | return m1.getOffset() - m2.getOffset(); | |
105 | } | |
106 | ||
107 | String name1= fLabelProvider.getText(e1); | |
108 | String name2= fLabelProvider.getText(e2); | |
109 | if (name1 == null) | |
110 | name1 = "";//$NON-NLS-1$ | |
111 | if (name2 == null) | |
112 | name2 = "";//$NON-NLS-1$ | |
104 | return m1.getOffset() - m2.getOffset(); | |
105 | } | |
106 | ||
107 | String name1= fLabelProvider.getText(e1); | |
108 | String name2= fLabelProvider.getText(e2); | |
109 | if (name1 == null) | |
110 | name1 = "";//$NON-NLS-1$ | |
111 | if (name2 == null) | |
112 | name2 = "";//$NON-NLS-1$ | |
113 | 113 | int result= getComparator().compare(name1, name2); |
114 | 114 | return result; |
115 | } | |
115 | } | |
116 | 116 | } |
117 | 117 | |
118 | 118 | private static final String KEY_SORTING= "org.eclipse.search.resultpage.sorting"; //$NON-NLS-1$ |
+9
-9
62 | 62 | public ReplaceConfigurationPage(ReplaceRefactoring refactoring) { |
63 | 63 | super("ReplaceConfigurationPage"); //$NON-NLS-1$ |
64 | 64 | fReplaceRefactoring= refactoring; |
65 | } | |
66 | ||
67 | @Override | |
65 | } | |
66 | ||
67 | @Override | |
68 | 68 | public void createControl(Composite parent) { |
69 | Composite result= new Composite(parent, SWT.NONE); | |
70 | GridLayout layout= new GridLayout(2, false); | |
69 | Composite result= new Composite(parent, SWT.NONE); | |
70 | GridLayout layout= new GridLayout(2, false); | |
71 | 71 | result.setLayout(layout); |
72 | 72 | |
73 | 73 | Label description= new Label(result, SWT.NONE); |
152 | 152 | Dialog.applyDialogFont(result); |
153 | 153 | |
154 | 154 | PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), ISearchHelpContextIds.REPLACE_DIALOG); |
155 | } | |
155 | } | |
156 | 156 | |
157 | 157 | final void updateOKStatus() { |
158 | 158 | RefactoringStatus status= new RefactoringStatus(); |
175 | 175 | fTextFieldContentAssist.setEnabled(enable); |
176 | 176 | } |
177 | 177 | |
178 | @Override | |
178 | @Override | |
179 | 179 | protected boolean performFinish() { |
180 | 180 | initializeRefactoring(); |
181 | 181 | storeSettings(); |
203 | 203 | IDialogSettings settings= SearchPlugin.getDefault().getDialogSettings().addNewSection(SETTINGS_GROUP); |
204 | 204 | settings.put(SETTINGS_REPLACE_WITH, history.toArray(new String[history.size()])); |
205 | 205 | |
206 | } | |
206 | } | |
207 | 207 | |
208 | 208 | private void initializeRefactoring() { |
209 | 209 | fReplaceRefactoring.setReplaceString(fTextField.getText()); |
210 | } | |
210 | } | |
211 | 211 | |
212 | 212 | }⏎ |
+9
-9
475 | 475 | replacementText= PatternConstructor.interpretReplaceEscapes(replacementText, originalText, lineDelimiter); |
476 | 476 | |
477 | 477 | Matcher matcher= pattern.matcher(originalText); |
478 | StringBuffer sb = new StringBuffer(); | |
479 | matcher.reset(); | |
480 | if (matcher.find()) { | |
481 | matcher.appendReplacement(sb, replacementText); | |
482 | } else { | |
483 | return null; | |
484 | } | |
485 | matcher.appendTail(sb); | |
486 | return sb.toString(); | |
478 | StringBuffer sb = new StringBuffer(); | |
479 | matcher.reset(); | |
480 | if (matcher.find()) { | |
481 | matcher.appendReplacement(sb, replacementText); | |
482 | } else { | |
483 | return null; | |
484 | } | |
485 | matcher.appendTail(sb); | |
486 | return sb.toString(); | |
487 | 487 | } catch (IndexOutOfBoundsException ex) { |
488 | 488 | throw new PatternSyntaxException(ex.getLocalizedMessage(), replacementText, -1); |
489 | 489 | } |
278 | 278 | ErrorDialog.openError(getShell(), SearchMessages.TextSearchPage_replace_searchproblems_title, SearchMessages.TextSearchPage_replace_searchproblems_message, e.getStatus()); |
279 | 279 | return false; |
280 | 280 | } |
281 | return true; | |
281 | return true; | |
282 | 282 | } |
283 | 283 | |
284 | 284 | @Override |
740 | 740 | }); |
741 | 741 | fSearchBinaryCheckbox.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 1, 1)); |
742 | 742 | fSearchBinaryCheckbox.setFont(searchInGroup.getFont()); |
743 | } | |
743 | } | |
744 | 744 | |
745 | 745 | /** |
746 | 746 | * Sets the search page's container. |
52 | 52 | * If every match should show up in the search result view then the match |
53 | 53 | * itself can be used as key. |
54 | 54 | * |
55 | * @param groupFactory the action group factory | |
56 | * or <code>null</code> if no factory is provided. | |
57 | * @param singularLabel the label to be used for this search occurrence | |
58 | * if there is one match | |
55 | * @param groupFactory the action group factory | |
56 | * or <code>null</code> if no factory is provided. | |
57 | * @param singularLabel the label to be used for this search occurrence | |
58 | * if there is one match | |
59 | 59 | * or <code>null</code> if the pluralLabelPattern should be used |
60 | * @param pluralLabelPattern the label pattern to be used for this search occurrence | |
61 | * if there are more than one matches or none. | |
62 | * This string may contain {0} which will be replace by the match count | |
63 | * @param imageDescriptor the image descriptor to be used for this search occurrence, | |
60 | * @param pluralLabelPattern the label pattern to be used for this search occurrence | |
61 | * if there are more than one matches or none. | |
62 | * This string may contain {0} which will be replace by the match count | |
63 | * @param imageDescriptor the image descriptor to be used for this search occurrence, | |
64 | 64 | * or <code>null</code> if this search should not have an image |
65 | 65 | * @param pageId the id of the search page which started the search |
66 | 66 | * @param labelProvider the label provider used by this search result view |
67 | * or <code>null</code> if the default provider should be used. | |
67 | * or <code>null</code> if the default provider should be used. | |
68 | 68 | * The default label provider shows the resource name and the corresponding image. |
69 | 69 | * @param gotoAction the action used by the view to go to a marker |
70 | 70 | * @param groupByKeyComputer the computer used by the view to compute the key for a marker |
71 | 71 | * @param operation the runnable used by the view to repeat the search |
72 | 72 | * |
73 | 73 | * @see IActionGroupFactory |
74 | * @since 2.0 | |
74 | * @since 2.0 | |
75 | 75 | */ |
76 | 76 | public void searchStarted( |
77 | 77 | IActionGroupFactory groupFactory, |
199 | 199 | * |
200 | 200 | * @param description the text description of the match |
201 | 201 | * @param groupByKey the <code>Object</code> by which this match is grouped |
202 | * @param marker the marker for this match | |
202 | * @param marker the marker for this match | |
203 | 203 | * @param resource the marker's resource passed for optimization |
204 | 204 | */ |
205 | 205 | public void addMatch(String description, Object groupByKey, IResource resource, IMarker marker); |
13 | 13 | <parent> |
14 | 14 | <artifactId>tests-pom</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | <relativePath>../tests-pom/</relativePath> |
18 | 18 | </parent> |
19 | 19 | <groupId>org.eclipse.search</groupId> |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %pluginName |
3 | 3 | Bundle-SymbolicName: org.eclipse.text |
4 | Bundle-Version: 3.8.100.qualifier | |
4 | Bundle-Version: 3.8.200.qualifier | |
5 | 5 | Bundle-Vendor: %providerName |
6 | 6 | Bundle-Localization: plugin |
7 | 7 | Export-Package: |
13 | 13 | <parent> |
14 | 14 | <artifactId>eclipse.platform.text</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | </parent> |
18 | 18 | <groupId>org.eclipse.text</groupId> |
19 | 19 | <artifactId>org.eclipse.text</artifactId> |
20 | <version>3.8.100-SNAPSHOT</version> | |
20 | <version>3.8.200-SNAPSHOT</version> | |
21 | 21 | <packaging>eclipse-plugin</packaging> |
22 | 22 | </project> |
244 | 244 | * Returns all positions managed by the document grouped by category. |
245 | 245 | * |
246 | 246 | * @return the document's positions |
247 | */ | |
247 | */ | |
248 | 248 | protected Map<String, List<Position>> getDocumentManagedPositions() { |
249 | 249 | return fPositions; |
250 | 250 | } |
68 | 68 | * @param detail the detailed message |
69 | 69 | */ |
70 | 70 | public AssertionFailedException(String detail) { |
71 | super(detail); | |
71 | super(detail); | |
72 | 72 | } |
73 | 73 | } |
74 | 74 |
329 | 329 | * @since 3.3 |
330 | 330 | */ |
331 | 331 | private char[] allocate(int size) { |
332 | return new char[size]; | |
333 | } | |
332 | return new char[size]; | |
333 | } | |
334 | 334 | |
335 | 335 | /* |
336 | 336 | * Executes System.arraycopy if length != 0. A length < 0 cannot happen -> don't hide coding |
627 | 627 | |
628 | 628 | /** |
629 | 629 | * Returns the line delimiter of that line or <code>null</code> if the |
630 | * line is not closed with a line delimiter. | |
630 | * line is not closed with a line delimiter. | |
631 | 631 | * |
632 | 632 | * @param line the line of interest |
633 | * @return the line's delimiter or <code>null</code> if line does not have a delimiter | |
633 | * @return the line's delimiter or <code>null</code> if line does not have a delimiter | |
634 | 634 | * @exception BadLocationException if the line number is invalid in this document |
635 | 635 | */ |
636 | 636 | String getLineDelimiter(int line) throws BadLocationException; |
47 | 47 | String[] getManagingPositionCategories(); |
48 | 48 | |
49 | 49 | |
50 | /* zero-length partition support */ | |
50 | /* zero-length partition support */ | |
51 | 51 | |
52 | /** | |
52 | /** | |
53 | 53 | * Returns the content type of the partition containing the given offset in |
54 | 54 | * the connected document. There must be a document connected to this |
55 | 55 | * partitioner. |
70 | 70 | * a delimited partition starting at <code>offset</code> |
71 | 71 | * @return the content type of the offset's partition |
72 | 72 | */ |
73 | String getContentType(int offset, boolean preferOpenPartitions); | |
73 | String getContentType(int offset, boolean preferOpenPartitions); | |
74 | 74 | |
75 | /** | |
75 | /** | |
76 | 76 | * Returns the partition containing the given offset of the connected |
77 | 77 | * document. There must be a document connected to this partitioner. |
78 | 78 | * <p> |
92 | 92 | * a delimited partition starting at <code>offset</code> |
93 | 93 | * @return the partition containing the offset |
94 | 94 | */ |
95 | ITypedRegion getPartition(int offset, boolean preferOpenPartitions); | |
95 | ITypedRegion getPartition(int offset, boolean preferOpenPartitions); | |
96 | 96 | |
97 | /** | |
97 | /** | |
98 | 98 | * Returns the partitioning of the given range of the connected document. |
99 | 99 | * There must be a document connected to this partitioner. |
100 | 100 | * <p> |
117 | 117 | * partitioning |
118 | 118 | * @return the partitioning of the range |
119 | 119 | */ |
120 | ITypedRegion[] computePartitioning(int offset, int length, boolean includeZeroLengthPartitions); | |
120 | ITypedRegion[] computePartitioning(int offset, int length, boolean includeZeroLengthPartitions); | |
121 | 121 | } |
28 | 28 | */ |
29 | 29 | public interface ISynchronizable { |
30 | 30 | |
31 | /** | |
31 | /** | |
32 | 32 | * Sets the lock object for this object. If the lock object is not |
33 | 33 | * <code>null</code> subsequent calls to specified methods of this object |
34 | 34 | * are synchronized on this lock object. Which methods are synchronized is |
40 | 40 | * |
41 | 41 | * @param lockObject the lock object. May be <code>null</code>. |
42 | 42 | */ |
43 | void setLockObject(Object lockObject); | |
43 | void setLockObject(Object lockObject); | |
44 | 44 | |
45 | /** | |
45 | /** | |
46 | 46 | * Returns the lock object or <code>null</code> if there is none. Clients |
47 | 47 | * should use the lock object in order to synchronize concurrent access to |
48 | 48 | * the implementer. |
49 | 49 | * |
50 | 50 | * @return the lock object or <code>null</code> |
51 | 51 | */ |
52 | Object getLockObject(); | |
52 | Object getLockObject(); | |
53 | 53 | } |
0 | 0 | /******************************************************************************* |
1 | * Copyright (c) 2000, 2008 IBM Corporation and others. | |
1 | * Copyright (c) 2000, 2019 IBM Corporation and others. | |
2 | 2 | * |
3 | 3 | * This program and the accompanying materials |
4 | 4 | * are made available under the terms of the Eclipse Public License 2.0 |
329 | 329 | if (text != null) { |
330 | 330 | fTextLength= text.length(); |
331 | 331 | createLines(text, 0, 0); |
332 | } else { | |
333 | fTextLength= 0; | |
332 | 334 | } |
333 | 335 | } |
334 | 336 |
171 | 171 | * |
172 | 172 | * @param tracker the list line tracker |
173 | 173 | */ |
174 | TreeLineTracker(ListLineTracker tracker) { | |
175 | final List<Line> lines= tracker.getLines(); | |
176 | final int n= lines.size(); | |
177 | if (n == 0) | |
178 | return; | |
179 | ||
180 | Line line= lines.get(0); | |
181 | String delim= line.delimiter; | |
182 | if (delim == null) | |
183 | delim= NO_DELIM; | |
184 | int length= line.length; | |
185 | fRoot= new Node(length, delim); | |
186 | Node node= fRoot; | |
174 | TreeLineTracker(ListLineTracker tracker) { | |
175 | final List<Line> lines= tracker.getLines(); | |
176 | final int n= lines.size(); | |
177 | if (n == 0) | |
178 | return; | |
179 | ||
180 | Line line= lines.get(0); | |
181 | String delim= line.delimiter; | |
182 | if (delim == null) | |
183 | delim= NO_DELIM; | |
184 | int length= line.length; | |
185 | fRoot= new Node(length, delim); | |
186 | Node node= fRoot; | |
187 | 187 | |
188 | 188 | for (int i= 1; i < n; i++) { |
189 | line= lines.get(i); | |
190 | delim= line.delimiter; | |
191 | if (delim == null) | |
192 | delim= NO_DELIM; | |
193 | length= line.length; | |
189 | line= lines.get(i); | |
190 | delim= line.delimiter; | |
191 | if (delim == null) | |
192 | delim= NO_DELIM; | |
193 | length= line.length; | |
194 | 194 | node= insertAfter(node, length, delim); |
195 | } | |
195 | } | |
196 | 196 | |
197 | 197 | if (node.delimiter != NO_DELIM) |
198 | 198 | insertAfter(node, 0, NO_DELIM); |
199 | 199 | |
200 | 200 | if (ASSERT) checkTree(); |
201 | } | |
201 | } | |
202 | 202 | |
203 | 203 | /** |
204 | 204 | * Returns the node (line) including a certain offset. If the offset is between two |
174 | 174 | } |
175 | 175 | } |
176 | 176 | |
177 | /** | |
178 | * Tries to nest the given <code>LinkedModeModel</code> onto the top of | |
179 | * the stack of environments managed by the receiver. If <code>force</code> | |
180 | * is <code>true</code>, any environments on the stack that create a conflict | |
181 | * are killed. | |
182 | * | |
183 | * @param model the model to nest | |
184 | * @param force whether to force the addition of the model | |
185 | * @return <code>true</code> if nesting was successful, <code>false</code> otherwise (only possible if <code>force</code> is <code>false</code> | |
186 | */ | |
187 | public boolean nestEnvironment(LinkedModeModel model, boolean force) { | |
188 | Assert.isNotNull(model); | |
189 | ||
190 | try { | |
191 | while (true) { | |
192 | if (fEnvironments.isEmpty()) { | |
193 | model.addLinkingListener(fListener); | |
194 | fEnvironments.push(model); | |
195 | return true; | |
196 | } | |
197 | ||
198 | LinkedModeModel top= fEnvironments.peek(); | |
199 | if (model.canNestInto(top)) { | |
200 | model.addLinkingListener(fListener); | |
201 | fEnvironments.push(model); | |
202 | return true; | |
203 | } else if (!force) { | |
204 | return false; | |
205 | } else { // force | |
206 | fEnvironments.pop(); | |
207 | top.exit(ILinkedModeListener.NONE); | |
208 | // continue; | |
209 | } | |
210 | } | |
211 | } finally { | |
212 | // if we remove any, make sure the new one got inserted | |
213 | Assert.isTrue(fEnvironments.size() > 0); | |
214 | } | |
215 | } | |
177 | /** | |
178 | * Tries to nest the given <code>LinkedModeModel</code> onto the top of | |
179 | * the stack of environments managed by the receiver. If <code>force</code> | |
180 | * is <code>true</code>, any environments on the stack that create a conflict | |
181 | * are killed. | |
182 | * | |
183 | * @param model the model to nest | |
184 | * @param force whether to force the addition of the model | |
185 | * @return <code>true</code> if nesting was successful, <code>false</code> otherwise (only possible if <code>force</code> is <code>false</code> | |
186 | */ | |
187 | public boolean nestEnvironment(LinkedModeModel model, boolean force) { | |
188 | Assert.isNotNull(model); | |
189 | ||
190 | try { | |
191 | while (true) { | |
192 | if (fEnvironments.isEmpty()) { | |
193 | model.addLinkingListener(fListener); | |
194 | fEnvironments.push(model); | |
195 | return true; | |
196 | } | |
197 | ||
198 | LinkedModeModel top= fEnvironments.peek(); | |
199 | if (model.canNestInto(top)) { | |
200 | model.addLinkingListener(fListener); | |
201 | fEnvironments.push(model); | |
202 | return true; | |
203 | } else if (!force) { | |
204 | return false; | |
205 | } else { // force | |
206 | fEnvironments.pop(); | |
207 | top.exit(ILinkedModeListener.NONE); | |
208 | // continue; | |
209 | } | |
210 | } | |
211 | } finally { | |
212 | // if we remove any, make sure the new one got inserted | |
213 | Assert.isTrue(fEnvironments.size() > 0); | |
214 | } | |
215 | } | |
216 | 216 | |
217 | 217 | /** |
218 | 218 | * Returns the <code>LinkedModeModel</code> that is on top of the stack of |
504 | 504 | // register positions |
505 | 505 | try { |
506 | 506 | for (LinkedPositionGroup group : fGroups) { |
507 | group.register(this); | |
508 | } | |
507 | group.register(this); | |
508 | } | |
509 | 509 | return true; |
510 | 510 | } catch (BadLocationException e){ |
511 | 511 | // if we fail to add, make sure to release all listeners again |
519 | 519 | * model, throws an IllegalStateException otherwise. |
520 | 520 | */ |
521 | 521 | private void enforceNotEmpty() { |
522 | boolean hasPosition= false; | |
522 | boolean hasPosition= false; | |
523 | 523 | for (LinkedPositionGroup linkedPositionGroup : fGroups) |
524 | 524 | if (!linkedPositionGroup.isEmpty()) { |
525 | 525 | hasPosition= true; |
528 | 528 | if (!hasPosition) |
529 | 529 | throw new IllegalStateException("must specify at least one linked position"); //$NON-NLS-1$ |
530 | 530 | |
531 | } | |
532 | ||
533 | /** | |
531 | } | |
532 | ||
533 | /** | |
534 | 534 | * Collects all the documents that contained positions are set upon. |
535 | * @return the set of documents affected by this model | |
536 | */ | |
537 | private IDocument[] getDocuments() { | |
538 | Set<IDocument> docs= new HashSet<>(); | |
539 | for (LinkedPositionGroup group : fGroups) { | |
540 | docs.addAll(Arrays.asList(group.getDocuments())); | |
541 | } | |
542 | return docs.toArray(new IDocument[docs.size()]); | |
543 | } | |
544 | ||
545 | /** | |
546 | * Returns whether the receiver can be nested into the given <code>parent</code> | |
547 | * model. If yes, the parent model and its position that the receiver | |
548 | * fits in are remembered. | |
549 | * | |
550 | * @param parent the parent model candidate | |
551 | * @return <code>true</code> if the receiver can be nested into <code>parent</code>, <code>false</code> otherwise | |
552 | */ | |
553 | boolean canNestInto(LinkedModeModel parent) { | |
554 | for (LinkedPositionGroup group : fGroups) { | |
535 | * @return the set of documents affected by this model | |
536 | */ | |
537 | private IDocument[] getDocuments() { | |
538 | Set<IDocument> docs= new HashSet<>(); | |
539 | for (LinkedPositionGroup group : fGroups) { | |
540 | docs.addAll(Arrays.asList(group.getDocuments())); | |
541 | } | |
542 | return docs.toArray(new IDocument[docs.size()]); | |
543 | } | |
544 | ||
545 | /** | |
546 | * Returns whether the receiver can be nested into the given <code>parent</code> | |
547 | * model. If yes, the parent model and its position that the receiver | |
548 | * fits in are remembered. | |
549 | * | |
550 | * @param parent the parent model candidate | |
551 | * @return <code>true</code> if the receiver can be nested into <code>parent</code>, <code>false</code> otherwise | |
552 | */ | |
553 | boolean canNestInto(LinkedModeModel parent) { | |
554 | for (LinkedPositionGroup group : fGroups) { | |
555 | 555 | if (!enforceNestability(group, parent)) { |
556 | 556 | fParentPosition= null; |
557 | 557 | return false; |
558 | 558 | } |
559 | 559 | } |
560 | 560 | |
561 | Assert.isNotNull(fParentPosition); | |
562 | fParentEnvironment= parent; | |
563 | return true; | |
564 | } | |
565 | ||
566 | /** | |
561 | Assert.isNotNull(fParentPosition); | |
562 | fParentEnvironment= parent; | |
563 | return true; | |
564 | } | |
565 | ||
566 | /** | |
567 | 567 | * Called by nested models when a group is added to them. All |
568 | 568 | * positions in all groups of a nested model have to fit inside a |
569 | 569 | * single position in the parent model. |
320 | 320 | |
321 | 321 | void register(LinkedModeModel model) throws BadLocationException { |
322 | 322 | for (LinkedPosition pos : fPositions) { |
323 | model.register(pos); | |
324 | } | |
323 | model.register(pos); | |
324 | } | |
325 | 325 | } |
326 | 326 | |
327 | 327 | /** |
30 | 30 | */ |
31 | 31 | class AnnotationMap implements IAnnotationMap { |
32 | 32 | |
33 | /** | |
34 | * The lock object used to synchronize the operations explicitly defined by | |
35 | * <code>IAnnotationMap</code> | |
36 | */ | |
37 | private Object fLockObject; | |
38 | /** | |
39 | * The internal lock object used if <code>fLockObject</code> is <code>null</code>. | |
33 | /** | |
34 | * The lock object used to synchronize the operations explicitly defined by | |
35 | * <code>IAnnotationMap</code> | |
36 | */ | |
37 | private Object fLockObject; | |
38 | /** | |
39 | * The internal lock object used if <code>fLockObject</code> is <code>null</code>. | |
40 | 40 | * @since 3.2 |
41 | 41 | */ |
42 | private final Object fInternalLockObject= new Object(); | |
42 | private final Object fInternalLockObject= new Object(); | |
43 | 43 | |
44 | /** The map holding the annotations */ | |
45 | private Map<Annotation, Position> fInternalMap; | |
44 | /** The map holding the annotations */ | |
45 | private Map<Annotation, Position> fInternalMap; | |
46 | 46 | |
47 | /** | |
48 | * Creates a new annotation map with the given capacity. | |
49 | * | |
50 | * @param capacity the capacity | |
51 | */ | |
52 | public AnnotationMap(int capacity) { | |
53 | fInternalMap= new HashMap<>(capacity); | |
54 | } | |
47 | /** | |
48 | * Creates a new annotation map with the given capacity. | |
49 | * | |
50 | * @param capacity the capacity | |
51 | */ | |
52 | public AnnotationMap(int capacity) { | |
53 | fInternalMap= new HashMap<>(capacity); | |
54 | } | |
55 | 55 | |
56 | @Override | |
56 | @Override | |
57 | 57 | public synchronized void setLockObject(Object lockObject) { |
58 | fLockObject= lockObject; | |
59 | } | |
58 | fLockObject= lockObject; | |
59 | } | |
60 | 60 | |
61 | @Override | |
61 | @Override | |
62 | 62 | public synchronized Object getLockObject() { |
63 | if (fLockObject == null) | |
64 | return fInternalLockObject; | |
65 | return fLockObject; | |
66 | } | |
63 | if (fLockObject == null) | |
64 | return fInternalLockObject; | |
65 | return fLockObject; | |
66 | } | |
67 | 67 | |
68 | @Override | |
68 | @Override | |
69 | 69 | public Iterator<Position> valuesIterator() { |
70 | synchronized (getLockObject()) { | |
71 | return new ArrayList<>(fInternalMap.values()).iterator(); | |
72 | } | |
73 | } | |
70 | synchronized (getLockObject()) { | |
71 | return new ArrayList<>(fInternalMap.values()).iterator(); | |
72 | } | |
73 | } | |
74 | 74 | |
75 | @Override | |
75 | @Override | |
76 | 76 | public Iterator<Annotation> keySetIterator() { |
77 | synchronized (getLockObject()) { | |
78 | return new ArrayList<>(fInternalMap.keySet()).iterator(); | |
79 | } | |
80 | } | |
77 | synchronized (getLockObject()) { | |
78 | return new ArrayList<>(fInternalMap.keySet()).iterator(); | |
79 | } | |
80 | } | |
81 | 81 | |
82 | @Override | |
82 | @Override | |
83 | 83 | public boolean containsKey(Object annotation) { |
84 | synchronized (getLockObject()) { | |
85 | return fInternalMap.containsKey(annotation); | |
86 | } | |
87 | } | |
84 | synchronized (getLockObject()) { | |
85 | return fInternalMap.containsKey(annotation); | |
86 | } | |
87 | } | |
88 | 88 | |
89 | @Override | |
89 | @Override | |
90 | 90 | public Position put(Annotation annotation, Position position) { |
91 | synchronized (getLockObject()) { | |
92 | return fInternalMap.put(annotation, position); | |
93 | } | |
94 | } | |
91 | synchronized (getLockObject()) { | |
92 | return fInternalMap.put(annotation, position); | |
93 | } | |
94 | } | |
95 | 95 | |
96 | @Override | |
96 | @Override | |
97 | 97 | public Position get(Object annotation) { |
98 | synchronized (getLockObject()) { | |
99 | return fInternalMap.get(annotation); | |
100 | } | |
101 | } | |
98 | synchronized (getLockObject()) { | |
99 | return fInternalMap.get(annotation); | |
100 | } | |
101 | } | |
102 | 102 | |
103 | @Override | |
103 | @Override | |
104 | 104 | public void clear() { |
105 | synchronized (getLockObject()) { | |
106 | fInternalMap.clear(); | |
107 | } | |
108 | } | |
105 | synchronized (getLockObject()) { | |
106 | fInternalMap.clear(); | |
107 | } | |
108 | } | |
109 | 109 | |
110 | @Override | |
110 | @Override | |
111 | 111 | public Position remove(Object annotation) { |
112 | synchronized (getLockObject()) { | |
113 | return fInternalMap.remove(annotation); | |
114 | } | |
115 | } | |
112 | synchronized (getLockObject()) { | |
113 | return fInternalMap.remove(annotation); | |
114 | } | |
115 | } | |
116 | 116 | |
117 | @Override | |
117 | @Override | |
118 | 118 | public int size() { |
119 | synchronized (getLockObject()) { | |
120 | return fInternalMap.size(); | |
121 | } | |
122 | } | |
119 | synchronized (getLockObject()) { | |
120 | return fInternalMap.size(); | |
121 | } | |
122 | } | |
123 | 123 | |
124 | @Override | |
124 | @Override | |
125 | 125 | public boolean isEmpty() { |
126 | synchronized (getLockObject()) { | |
126 | synchronized (getLockObject()) { | |
127 | 127 | return fInternalMap.isEmpty(); |
128 | 128 | } |
129 | } | |
129 | } | |
130 | 130 | |
131 | 131 | @Override |
132 | 132 | public boolean containsValue(Object value) { |
329 | 329 | return (IAnnotationMap) fAnnotations; |
330 | 330 | } |
331 | 331 | |
332 | @Override | |
332 | @Override | |
333 | 333 | public Object getLockObject() { |
334 | return getAnnotationMap().getLockObject(); | |
335 | } | |
336 | ||
337 | @Override | |
334 | return getAnnotationMap().getLockObject(); | |
335 | } | |
336 | ||
337 | @Override | |
338 | 338 | public void setLockObject(Object lockObject) { |
339 | getAnnotationMap().setLockObject(lockObject); | |
340 | } | |
341 | ||
342 | /** | |
343 | * Returns the current annotation model event. This is the event that will be sent out | |
344 | * when calling <code>fireModelChanged</code>. | |
345 | * | |
346 | * @return the current annotation model event | |
347 | * @since 3.0 | |
348 | */ | |
349 | protected final AnnotationModelEvent getAnnotationModelEvent() { | |
350 | synchronized (getLockObject()) { | |
351 | if (fModelEvent == null) { | |
352 | fModelEvent= createAnnotationModelEvent(); | |
353 | fModelEvent.markWorldChange(false); | |
354 | fModificationStamp= new Object(); | |
355 | } | |
356 | return fModelEvent; | |
357 | } | |
358 | } | |
339 | getAnnotationMap().setLockObject(lockObject); | |
340 | } | |
341 | ||
342 | /** | |
343 | * Returns the current annotation model event. This is the event that will be sent out | |
344 | * when calling <code>fireModelChanged</code>. | |
345 | * | |
346 | * @return the current annotation model event | |
347 | * @since 3.0 | |
348 | */ | |
349 | protected final AnnotationModelEvent getAnnotationModelEvent() { | |
350 | synchronized (getLockObject()) { | |
351 | if (fModelEvent == null) { | |
352 | fModelEvent= createAnnotationModelEvent(); | |
353 | fModelEvent.markWorldChange(false); | |
354 | fModificationStamp= new Object(); | |
355 | } | |
356 | return fModelEvent; | |
357 | } | |
358 | } | |
359 | 359 | |
360 | 360 | @Override |
361 | 361 | public void addAnnotation(Annotation annotation, Position position) { |
441 | 441 | IAnnotationModelListenerExtension extension= (IAnnotationModelListenerExtension) listener; |
442 | 442 | AnnotationModelEvent event= createAnnotationModelEvent(); |
443 | 443 | event.markSealed(); |
444 | extension.modelChanged(event); | |
444 | extension.modelChanged(event); | |
445 | 445 | } else |
446 | listener.modelChanged(this); | |
446 | listener.modelChanged(this); | |
447 | 447 | } |
448 | 448 | } |
449 | 449 |
35 | 35 | * @param string the string |
36 | 36 | * @param variables the variable positions |
37 | 37 | */ |
38 | public TemplateBuffer(String string, TemplateVariable[] variables) { | |
38 | public TemplateBuffer(String string, TemplateVariable[] variables) { | |
39 | 39 | setContent(string, variables); |
40 | } | |
40 | } | |
41 | 41 | |
42 | 42 | /** |
43 | 43 | * Sets the content of the template buffer. |
84 | 84 | * @return the id of the receiver |
85 | 85 | */ |
86 | 86 | public String getId() { |
87 | return fId; | |
87 | return fId; | |
88 | 88 | } |
89 | 89 | |
90 | 90 | |
234 | 234 | List<RangeMarker> positions= variablesToPositions(variables); |
235 | 235 | List<ReplaceEdit> edits= new ArrayList<>(5); |
236 | 236 | |
237 | // iterate over all variables and try to resolve them | |
238 | for (int i= 0; i != variables.length; i++) { | |
239 | TemplateVariable variable= variables[i]; | |
237 | // iterate over all variables and try to resolve them | |
238 | for (int i= 0; i != variables.length; i++) { | |
239 | TemplateVariable variable= variables[i]; | |
240 | 240 | |
241 | 241 | if (!variable.isResolved()) |
242 | 242 | resolve(variable, context); |
247 | 247 | for (int k= 0; k != offsets.length; k++) |
248 | 248 | edits.add(new ReplaceEdit(offsets[k], variable.getInitialLength(), value)); |
249 | 249 | |
250 | } | |
251 | ||
252 | IDocument document= new Document(buffer.getString()); | |
253 | MultiTextEdit edit= new MultiTextEdit(0, document.getLength()); | |
254 | edit.addChildren(positions.toArray(new TextEdit[positions.size()])); | |
255 | edit.addChildren(edits.toArray(new TextEdit[edits.size()])); | |
256 | edit.apply(document, TextEdit.UPDATE_REGIONS); | |
250 | } | |
251 | ||
252 | IDocument document= new Document(buffer.getString()); | |
253 | MultiTextEdit edit= new MultiTextEdit(0, document.getLength()); | |
254 | edit.addChildren(positions.toArray(new TextEdit[positions.size()])); | |
255 | edit.addChildren(edits.toArray(new TextEdit[edits.size()])); | |
256 | edit.apply(document, TextEdit.UPDATE_REGIONS); | |
257 | 257 | |
258 | 258 | positionsToVariables(positions, variables); |
259 | 259 | |
260 | buffer.setContent(document.get(), variables); | |
261 | } | |
260 | buffer.setContent(document.get(), variables); | |
261 | } | |
262 | 262 | |
263 | 263 | /** |
264 | 264 | * Resolves a single variable in a context. Resolving is delegated to the registered resolver. |
276 | 276 | } |
277 | 277 | |
278 | 278 | private static List<RangeMarker> variablesToPositions(TemplateVariable[] variables) { |
279 | List<RangeMarker> positions= new ArrayList<>(5); | |
279 | List<RangeMarker> positions= new ArrayList<>(5); | |
280 | 280 | for (int i= 0; i != variables.length; i++) { |
281 | int[] offsets= variables[i].getOffsets(); | |
282 | for (int j= 0; j != offsets.length; j++) | |
281 | int[] offsets= variables[i].getOffsets(); | |
282 | for (int j= 0; j != offsets.length; j++) | |
283 | 283 | positions.add(new RangeMarker(offsets[j], 0)); |
284 | 284 | } |
285 | 285 | |
290 | 290 | Iterator<RangeMarker> iterator= positions.iterator(); |
291 | 291 | |
292 | 292 | for (int i= 0; i != variables.length; i++) { |
293 | TemplateVariable variable= variables[i]; | |
293 | TemplateVariable variable= variables[i]; | |
294 | 294 | |
295 | 295 | int[] offsets= new int[variable.getOffsets().length]; |
296 | 296 | for (int j= 0; j != offsets.length; j++) |
140 | 140 | * @return the type name of the variable |
141 | 141 | */ |
142 | 142 | public String getType() { |
143 | return fType.getName(); | |
143 | return fType.getName(); | |
144 | 144 | } |
145 | 145 | |
146 | 146 | /** |
159 | 159 | * @return the name of the variable |
160 | 160 | */ |
161 | 161 | public String getName() { |
162 | return fName; | |
162 | return fName; | |
163 | 163 | } |
164 | 164 | |
165 | 165 | /** |
250 | 250 | * @param unambiguous the new unambiguous state of the variable |
251 | 251 | */ |
252 | 252 | public void setUnambiguous(boolean unambiguous) { |
253 | fIsUnambiguous= unambiguous; | |
254 | if (unambiguous) | |
255 | setResolved(true); | |
253 | fIsUnambiguous= unambiguous; | |
254 | if (unambiguous) | |
255 | setResolved(true); | |
256 | 256 | } |
257 | 257 | |
258 | 258 | /** |
109 | 109 | * @since 3.3 |
110 | 110 | */ |
111 | 111 | public boolean removeTextEdit(TextEdit edit) { |
112 | return fEdits.remove(edit); | |
112 | return fEdits.remove(edit); | |
113 | 113 | } |
114 | 114 | |
115 | 115 | /** |
118 | 118 | * @since 3.3 |
119 | 119 | */ |
120 | 120 | public void clearTextEdits() { |
121 | fEdits.clear(); | |
121 | fEdits.clear(); | |
122 | 122 | } |
123 | 123 | |
124 | 124 |
126 | 126 | @Override |
127 | 127 | public void preferenceChange(PreferenceChangeEvent event) { |
128 | 128 | /* |
129 | * Don't load if we are in the process of saving ourselves. We are in sync anyway after the | |
130 | * save operation, and clients may trigger reloading by listening to preference store | |
131 | * updates. | |
132 | */ | |
133 | if (!fIgnorePreferenceStoreChanges && fKey.equals(event.getKey())) | |
134 | try { | |
135 | load(); | |
136 | } catch (IOException x) { | |
137 | handleException(x); | |
138 | } | |
129 | * Don't load if we are in the process of saving ourselves. We are in sync anyway after the | |
130 | * save operation, and clients may trigger reloading by listening to preference store | |
131 | * updates. | |
132 | */ | |
133 | if (!fIgnorePreferenceStoreChanges && fKey.equals(event.getKey())) | |
134 | try { | |
135 | load(); | |
136 | } catch (IOException x) { | |
137 | handleException(x); | |
138 | } | |
139 | 139 | } |
140 | 140 | }; |
141 | 141 | fPreferenceStore.addPreferenceChangeListener(fPropertyListener); |
140 | 140 | */ |
141 | 141 | protected void undoTextChange() { |
142 | 142 | try { |
143 | if (fDocumentUndoManager.fDocument instanceof IDocumentExtension4) | |
143 | if (fDocumentUndoManager.fDocument instanceof IDocumentExtension4) { | |
144 | 144 | ((IDocumentExtension4) fDocumentUndoManager.fDocument).replace(fStart, fText |
145 | 145 | .length(), fPreservedText, fUndoModificationStamp); |
146 | else | |
146 | } else { | |
147 | 147 | fDocumentUndoManager.fDocument.replace(fStart, fText.length(), |
148 | 148 | fPreservedText); |
149 | } | |
149 | 150 | } catch (BadLocationException x) { |
150 | 151 | } |
151 | 152 | } |
267 | 268 | */ |
268 | 269 | protected void redoTextChange() { |
269 | 270 | try { |
270 | if (fDocumentUndoManager.fDocument instanceof IDocumentExtension4) | |
271 | if (fDocumentUndoManager.fDocument instanceof IDocumentExtension4) { | |
271 | 272 | ((IDocumentExtension4) fDocumentUndoManager.fDocument).replace(fStart, fEnd - fStart, fText, fRedoModificationStamp); |
272 | else | |
273 | } else { | |
273 | 274 | fDocumentUndoManager.fDocument.replace(fStart, fEnd - fStart, fText); |
275 | } | |
274 | 276 | } catch (BadLocationException x) { |
275 | 277 | } |
276 | 278 | } |
313 | 315 | * @return a new, uncommitted text change or a compound text change |
314 | 316 | */ |
315 | 317 | protected UndoableTextChange createCurrent() { |
316 | if (fDocumentUndoManager.fFoldingIntoCompoundChange) | |
318 | if (fDocumentUndoManager.fFoldingIntoCompoundChange) { | |
317 | 319 | return new UndoableCompoundTextChange(fDocumentUndoManager); |
320 | } | |
318 | 321 | return new UndoableTextChange(fDocumentUndoManager); |
319 | 322 | } |
320 | 323 | |
452 | 455 | UndoableTextChange c; |
453 | 456 | |
454 | 457 | c= fChanges.get(0); |
455 | fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fPreservedText, c.fText, uiInfo, DocumentUndoEvent.ABOUT_TO_UNDO, true); | |
458 | fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fPreservedText, c.fText, uiInfo, DocumentUndoEvent.ABOUT_TO_UNDO, size > 1); | |
456 | 459 | |
457 | 460 | for (int i= size - 1; i >= 0; --i) { |
458 | 461 | c= fChanges.get(i); |
460 | 463 | } |
461 | 464 | fDocumentUndoManager.resetProcessChangeState(); |
462 | 465 | fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fPreservedText, c.fText, uiInfo, |
463 | DocumentUndoEvent.UNDONE, true); | |
466 | DocumentUndoEvent.UNDONE, size > 1); | |
464 | 467 | } |
465 | 468 | return Status.OK_STATUS; |
466 | 469 | } |
473 | 476 | |
474 | 477 | UndoableTextChange c; |
475 | 478 | c= fChanges.get(size - 1); |
476 | fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, uiInfo, DocumentUndoEvent.ABOUT_TO_REDO, true); | |
479 | fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, uiInfo, DocumentUndoEvent.ABOUT_TO_REDO, size > 1); | |
477 | 480 | |
478 | 481 | for (int i= 0; i <= size - 1; ++i) { |
479 | 482 | c= fChanges.get(i); |
480 | 483 | c.redoTextChange(); |
481 | 484 | } |
482 | 485 | fDocumentUndoManager.resetProcessChangeState(); |
483 | fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, uiInfo, DocumentUndoEvent.REDONE, true); | |
486 | fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, uiInfo, DocumentUndoEvent.REDONE, size > 1); | |
484 | 487 | } |
485 | 488 | |
486 | 489 | return Status.OK_STATUS; |
508 | 511 | @Override |
509 | 512 | protected UndoableTextChange createCurrent() { |
510 | 513 | |
511 | if (!fDocumentUndoManager.fFoldingIntoCompoundChange) | |
514 | if (!fDocumentUndoManager.fFoldingIntoCompoundChange) { | |
512 | 515 | return new UndoableTextChange(fDocumentUndoManager); |
516 | } | |
513 | 517 | |
514 | 518 | reinitialize(); |
515 | 519 | return this; |
518 | 522 | @Override |
519 | 523 | protected void commit() { |
520 | 524 | // if there is pending data, update the text change |
521 | if (fStart > -1) | |
525 | if (fStart > -1) { | |
522 | 526 | updateTextChange(); |
527 | } | |
523 | 528 | fDocumentUndoManager.fCurrent= createCurrent(); |
524 | 529 | fDocumentUndoManager.resetProcessChangeState(); |
525 | 530 | } |
531 | 536 | |
532 | 537 | @Override |
533 | 538 | protected long getUndoModificationStamp() { |
534 | if (fStart > -1) | |
539 | if (fStart > -1) { | |
535 | 540 | return super.getUndoModificationStamp(); |
536 | else if (fChanges.size() > 0) | |
541 | } else if (fChanges.size() > 0) { | |
537 | 542 | return fChanges.get(0) |
538 | 543 | .getUndoModificationStamp(); |
544 | } | |
539 | 545 | |
540 | 546 | return fUndoModificationStamp; |
541 | 547 | } |
542 | 548 | |
543 | 549 | @Override |
544 | 550 | protected long getRedoModificationStamp() { |
545 | if (fStart > -1) | |
551 | if (fStart > -1) { | |
546 | 552 | return super.getRedoModificationStamp(); |
547 | else if (fChanges.size() > 0) | |
553 | } else if (fChanges.size() > 0) { | |
548 | 554 | return fChanges.get(fChanges.size() - 1) |
549 | 555 | .getRedoModificationStamp(); |
556 | } | |
550 | 557 | |
551 | 558 | return fRedoModificationStamp; |
552 | 559 | } |
580 | 587 | // top operation but changes state. |
581 | 588 | IUndoableOperation op= fHistory.getUndoOperation(fUndoContext); |
582 | 589 | boolean wasValid= false; |
583 | if (op != null) | |
590 | if (op != null) { | |
584 | 591 | wasValid= op.canUndo(); |
592 | } | |
585 | 593 | // Process the change, providing the before and after timestamps |
586 | 594 | processChange(event.getOffset(), event.getOffset() |
587 | 595 | + event.getLength(), event.getText(), fReplacedText, |
597 | 605 | // created, then we should |
598 | 606 | // notify the history that the current operation changed if its |
599 | 607 | // validity has changed. |
600 | if (wasValid != fCurrent.isValid()) | |
608 | if (wasValid != fCurrent.isValid()) { | |
601 | 609 | fHistory.operationChanged(op); |
610 | } | |
602 | 611 | } else { |
603 | 612 | // if the change created a new fCurrent that we did not yet add |
604 | 613 | // to the |
780 | 789 | // single document change. |
781 | 790 | if (fLastAddedTextEdit != fCurrent) { |
782 | 791 | fCurrent.pretendCommit(); |
783 | if (fCurrent.isValid()) | |
792 | if (fCurrent.isValid()) { | |
784 | 793 | addToOperationHistory(fCurrent); |
794 | } | |
785 | 795 | } |
786 | 796 | fCurrent.commit(); |
787 | 797 | } |
809 | 819 | */ |
810 | 820 | @Override |
811 | 821 | public void redo() throws ExecutionException { |
812 | if (isConnected() && redoable()) | |
822 | if (isConnected() && redoable()) { | |
813 | 823 | OperationHistoryFactory.getOperationHistory().redo(getUndoContext(), null, null); |
824 | } | |
814 | 825 | } |
815 | 826 | |
816 | 827 | @Override |
817 | 828 | public void undo() throws ExecutionException { |
818 | if (undoable()) | |
829 | if (undoable()) { | |
819 | 830 | OperationHistoryFactory.getOperationHistory().undo(fUndoContext, null, null); |
831 | } | |
820 | 832 | } |
821 | 833 | |
822 | 834 | @Override |
824 | 836 | if (!isConnected()) { |
825 | 837 | initialize(); |
826 | 838 | } |
827 | if (!fConnected.contains(client)) | |
839 | if (!fConnected.contains(client)) { | |
828 | 840 | fConnected.add(client); |
841 | } | |
829 | 842 | } |
830 | 843 | |
831 | 844 | @Override |
923 | 936 | * Initializes the undo history. |
924 | 937 | */ |
925 | 938 | private void initializeUndoHistory() { |
926 | if (fHistory != null && fUndoContext != null) | |
939 | if (fHistory != null && fUndoContext != null) { | |
927 | 940 | fHistory.dispose(fUndoContext, true, true, false); |
941 | } | |
928 | 942 | |
929 | 943 | } |
930 | 944 | |
938 | 952 | */ |
939 | 953 | private boolean isWhitespaceText(String text) { |
940 | 954 | |
941 | if (text == null || text.length() == 0) | |
955 | if (text == null || text.length() == 0) { | |
942 | 956 | return false; |
957 | } | |
943 | 958 | |
944 | 959 | String[] delimiters= fDocument.getLegalLineDelimiters(); |
945 | 960 | int index= TextUtilities.startsWith(delimiters, text); |
948 | 963 | int length= text.length(); |
949 | 964 | for (int i= delimiters[index].length(); i < length; i++) { |
950 | 965 | c= text.charAt(i); |
951 | if (c != ' ' && c != '\t') | |
966 | if (c != ' ' && c != '\t') { | |
952 | 967 | return false; |
968 | } | |
953 | 969 | } |
954 | 970 | return true; |
955 | 971 | } |
982 | 998 | final long afterChangeModificationStamp) { |
983 | 999 | |
984 | 1000 | if (insertedText == null) |
1001 | { | |
985 | 1002 | insertedText= ""; //$NON-NLS-1$ |
1003 | } | |
986 | 1004 | |
987 | 1005 | if (replacedText == null) |
1006 | { | |
988 | 1007 | replacedText= ""; //$NON-NLS-1$ |
1008 | } | |
989 | 1009 | |
990 | 1010 | int length= insertedText.length(); |
991 | 1011 | int diff= modelEnd - modelStart; |
992 | 1012 | |
993 | if (fCurrent.fUndoModificationStamp == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) | |
1013 | if (fCurrent.fUndoModificationStamp == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) { | |
994 | 1014 | fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; |
1015 | } | |
995 | 1016 | |
996 | 1017 | // normalize |
997 | 1018 | if (diff < 0) { |
1008 | 1029 | || (modelStart != fCurrent.fStart |
1009 | 1030 | + fTextBuffer.length())) { |
1010 | 1031 | fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; |
1011 | if (fCurrent.attemptCommit()) | |
1032 | if (fCurrent.attemptCommit()) { | |
1012 | 1033 | fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; |
1034 | } | |
1013 | 1035 | |
1014 | 1036 | fInserting= true; |
1015 | 1037 | } |
1016 | if (fCurrent.fStart < 0) | |
1038 | if (fCurrent.fStart < 0) { | |
1017 | 1039 | fCurrent.fStart= fCurrent.fEnd= modelStart; |
1018 | if (length > 0) | |
1040 | } | |
1041 | if (length > 0) { | |
1019 | 1042 | fTextBuffer.append(insertedText); |
1043 | } | |
1020 | 1044 | } else if (length > 0) { |
1021 | 1045 | // by pasting or model manipulation |
1022 | 1046 | fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; |
1023 | if (fCurrent.attemptCommit()) | |
1047 | if (fCurrent.attemptCommit()) { | |
1024 | 1048 | fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; |
1049 | } | |
1025 | 1050 | |
1026 | 1051 | fCurrent.fStart= fCurrent.fEnd= modelStart; |
1027 | 1052 | fTextBuffer.append(insertedText); |
1028 | 1053 | fCurrent.fRedoModificationStamp= afterChangeModificationStamp; |
1029 | if (fCurrent.attemptCommit()) | |
1054 | if (fCurrent.attemptCommit()) { | |
1030 | 1055 | fCurrent.fUndoModificationStamp= afterChangeModificationStamp; |
1056 | } | |
1031 | 1057 | |
1032 | 1058 | } |
1033 | 1059 | } else { |
1067 | 1093 | // either DEL or backspace for the first time |
1068 | 1094 | |
1069 | 1095 | fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; |
1070 | if (fCurrent.attemptCommit()) | |
1096 | if (fCurrent.attemptCommit()) { | |
1071 | 1097 | fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; |
1098 | } | |
1072 | 1099 | |
1073 | 1100 | // as we can not decide whether it was DEL or backspace |
1074 | 1101 | // we initialize for backspace |
1082 | 1109 | } else if (length > 0) { |
1083 | 1110 | // whereby selection is not empty |
1084 | 1111 | fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; |
1085 | if (fCurrent.attemptCommit()) | |
1112 | if (fCurrent.attemptCommit()) { | |
1086 | 1113 | fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; |
1114 | } | |
1087 | 1115 | |
1088 | 1116 | fCurrent.fStart= modelStart; |
1089 | 1117 | fCurrent.fEnd= modelEnd; |
1103 | 1131 | || (modelStart != fCurrent.fStart |
1104 | 1132 | + fTextBuffer.length())) { |
1105 | 1133 | fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; |
1106 | if (fCurrent.attemptCommit()) | |
1134 | if (fCurrent.attemptCommit()) { | |
1107 | 1135 | fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; |
1136 | } | |
1108 | 1137 | |
1109 | 1138 | fOverwriting= true; |
1110 | 1139 | } |
1111 | 1140 | |
1112 | if (fCurrent.fStart < 0) | |
1141 | if (fCurrent.fStart < 0) { | |
1113 | 1142 | fCurrent.fStart= modelStart; |
1143 | } | |
1114 | 1144 | |
1115 | 1145 | fCurrent.fEnd= modelEnd; |
1116 | 1146 | fTextBuffer.append(insertedText); |
1121 | 1151 | } |
1122 | 1152 | // because of typing or pasting whereby selection is not empty |
1123 | 1153 | fCurrent.fRedoModificationStamp= beforeChangeModificationStamp; |
1124 | if (fCurrent.attemptCommit()) | |
1154 | if (fCurrent.attemptCommit()) { | |
1125 | 1155 | fCurrent.fUndoModificationStamp= beforeChangeModificationStamp; |
1156 | } | |
1126 | 1157 | |
1127 | 1158 | fCurrent.fStart= modelStart; |
1128 | 1159 | fCurrent.fEnd= modelEnd; |
1182 | 1213 | * clients, <code>false</code> if it is not |
1183 | 1214 | */ |
1184 | 1215 | boolean isConnected() { |
1185 | if (fConnected == null) | |
1216 | if (fConnected == null) { | |
1186 | 1217 | return false; |
1218 | } | |
1187 | 1219 | return !fConnected.isEmpty(); |
1188 | 1220 | } |
1189 | 1221 | |
1208 | 1240 | } |
1209 | 1241 | |
1210 | 1242 | IUndoableOperation op= OperationHistoryFactory.getOperationHistory().getUndoOperation(getUndoContext()); |
1211 | if (op != null && !(op instanceof UndoableTextChange)) | |
1243 | if (op != null && !(op instanceof UndoableTextChange)) { | |
1212 | 1244 | return; |
1245 | } | |
1213 | 1246 | |
1214 | 1247 | // Record the transfer itself as an undoable change. |
1215 | 1248 | // If the transfer results from some open operation, recording this change will |
1221 | 1254 | cmd.fText= cmd.fPreservedText= ""; //$NON-NLS-1$ |
1222 | 1255 | if (fDocument instanceof IDocumentExtension4) { |
1223 | 1256 | cmd.fRedoModificationStamp= ((IDocumentExtension4)fDocument).getModificationStamp(); |
1224 | if (op != null) | |
1257 | if (op != null) { | |
1225 | 1258 | cmd.fUndoModificationStamp= ((UndoableTextChange)op).fRedoModificationStamp; |
1259 | } | |
1226 | 1260 | } |
1227 | 1261 | addToOperationHistory(cmd); |
1228 | 1262 | } |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %Plugin.name |
3 | 3 | Bundle-SymbolicName: org.eclipse.text.tests |
4 | Bundle-Version: 3.12.100.qualifier | |
4 | Bundle-Version: 3.12.200.qualifier | |
5 | 5 | Bundle-Vendor: %Plugin.providerName |
6 | 6 | Bundle-Localization: plugin |
7 | 7 | Export-Package: |
13 | 13 | <parent> |
14 | 14 | <artifactId>tests-pom</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | <relativePath>../tests-pom/</relativePath> |
18 | 18 | </parent> |
19 | 19 | <groupId>org.eclipse.text</groupId> |
20 | 20 | <artifactId>org.eclipse.text.tests</artifactId> |
21 | <version>3.12.100-SNAPSHOT</version> | |
21 | <version>3.12.200-SNAPSHOT</version> | |
22 | 22 | <packaging>eclipse-test-plugin</packaging> |
23 | 23 | <properties> |
24 | 24 | <testSuite>${project.artifactId}</testSuite> |
49 | 49 | protected final void set(String string) { |
50 | 50 | fText.set(string); |
51 | 51 | fTracker.set(string); |
52 | } | |
52 | } | |
53 | 53 | |
54 | 54 | } |
156 | 156 | |
157 | 157 | @Test |
158 | 158 | public void testRemoveReallocateBeforeGap() throws Exception { |
159 | fText.replace(0, 0, "yyyyyzzzzz"); | |
160 | assertGap(10, 15); | |
161 | assertContents("yyyyyzzzzzxxxxx"); | |
159 | fText.replace(0, 0, "yyyyyzzzzz"); | |
160 | assertGap(10, 15); | |
161 | assertContents("yyyyyzzzzzxxxxx"); | |
162 | 162 | |
163 | fText.replace(2, 6, null); | |
164 | assertGap(2, 5); | |
165 | assertContents("yyzzxxxxx"); | |
166 | } | |
163 | fText.replace(2, 6, null); | |
164 | assertGap(2, 5); | |
165 | assertContents("yyzzxxxxx"); | |
166 | } | |
167 | 167 | } |
159 | 159 | |
160 | 160 | @Test |
161 | 161 | public void testRemoveReallocateBeforeGap() throws Exception { |
162 | fText.replace(0, 0, "yyyyyzzzzz"); | |
163 | assertGap(10, 15); | |
164 | assertContents("yyyyyzzzzzxxxxx"); | |
162 | fText.replace(0, 0, "yyyyyzzzzz"); | |
163 | assertGap(10, 15); | |
164 | assertContents("yyyyyzzzzzxxxxx"); | |
165 | 165 | |
166 | fText.replace(2, 6, null); | |
167 | assertGap(2, 7); | |
168 | assertContents("yyzzxxxxx"); | |
169 | } | |
166 | fText.replace(2, 6, null); | |
167 | assertGap(2, 7); | |
168 | assertContents("yyzzxxxxx"); | |
169 | } | |
170 | 170 | } |
0 | 0 | /******************************************************************************* |
1 | * Copyright (c) 2000, 2008 IBM Corporation and others. | |
1 | * Copyright (c) 2000, 2019 IBM Corporation and others. | |
2 | 2 | * |
3 | 3 | * This program and the accompanying materials |
4 | 4 | * are made available under the terms of the Eclipse Public License 2.0 |
381 | 381 | |
382 | 382 | @Test |
383 | 383 | public void testNoDelimiterLine() throws Exception { |
384 | set("abcef"); | |
385 | checkLines(new int[] { 5 }); | |
386 | ||
387 | replace(0, 0, ""); // switch to TreeLineTracker | |
388 | checkLines(new int[] { 5 }); | |
389 | } | |
384 | set("abcef"); | |
385 | checkLines(new int[] { 5 }); | |
386 | ||
387 | replace(0, 0, ""); // switch to TreeLineTracker | |
388 | checkLines(new int[] { 5 }); | |
389 | } | |
390 | 390 | |
391 | 391 | @Test |
392 | 392 | public void testFunnyLastLineCompatibility2() throws Exception { |
543 | 543 | } catch (BadLocationException e) { |
544 | 544 | } |
545 | 545 | } |
546 | ||
547 | /** | |
548 | * Test for Bug 545565. Some ListLineTracker methods yield wrong results after tracker content | |
549 | * was set to <code>null</code>. | |
550 | * | |
551 | * @throws BadLocationException if test failed | |
552 | */ | |
553 | @Test | |
554 | public void testBug545565_setNull() throws BadLocationException { | |
555 | int initialContentLength= fText.getLength(); | |
556 | set(null); | |
557 | assertEquals("Tracker not empty.", 1, fTracker.getNumberOfLines()); | |
558 | assertEquals("Tracker not empty.", 0, fTracker.getLineLength(0)); | |
559 | try { | |
560 | fTracker.getLineInformationOfOffset(5); | |
561 | fail("No exception for bad location."); | |
562 | } catch (BadLocationException e) { | |
563 | // expected | |
564 | } | |
565 | try { | |
566 | fTracker.getLineInformationOfOffset(initialContentLength); | |
567 | fail("No exception for bad location."); | |
568 | } catch (BadLocationException e) { | |
569 | // expected | |
570 | } | |
571 | try { | |
572 | fTracker.getLineNumberOfOffset(5); | |
573 | fail("No exception for bad location."); | |
574 | } catch (BadLocationException e) { | |
575 | // expected | |
576 | } | |
577 | try { | |
578 | fTracker.getLineNumberOfOffset(initialContentLength); | |
579 | fail("No exception for bad location."); | |
580 | } catch (BadLocationException e) { | |
581 | // expected | |
582 | } | |
583 | try { | |
584 | fTracker.getNumberOfLines(5, 3); | |
585 | fail("No exception for bad location."); | |
586 | } catch (BadLocationException e) { | |
587 | // expected | |
588 | } | |
589 | } | |
590 | ||
591 | /** | |
592 | * Check if ListLineTracker and TreeLineTracker return same result for same input in context of | |
593 | * Bug 545565. | |
594 | * | |
595 | * @throws BadLocationException if test fails | |
596 | */ | |
597 | @Test | |
598 | public void testBug545565_compareTrackerResult() throws BadLocationException { | |
599 | set(null); | |
600 | int lineFromListTracker= fTracker.getLineNumberOfOffset(0); | |
601 | replace(0, 0, null); | |
602 | int lineFromTreeTracker= fTracker.getLineNumberOfOffset(0); | |
603 | assertEquals("Trackers returned different lines for same offset.", lineFromTreeTracker, lineFromListTracker); | |
604 | } | |
546 | 605 | } |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %pluginName |
3 | 3 | Bundle-SymbolicName: org.eclipse.ui.editors; singleton:=true |
4 | Bundle-Version: 3.11.400.qualifier | |
4 | Bundle-Version: 3.11.500.qualifier | |
5 | 5 | Bundle-Activator: org.eclipse.ui.internal.editors.text.EditorsPlugin |
6 | 6 | Bundle-ActivationPolicy: lazy |
7 | 7 | Bundle-Vendor: %providerName |
13 | 13 | <parent> |
14 | 14 | <artifactId>eclipse.platform.text</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | </parent> |
18 | 18 | <groupId>org.eclipse.ui</groupId> |
19 | 19 | <artifactId>org.eclipse.ui.editors</artifactId> |
20 | <version>3.11.400-SNAPSHOT</version> | |
20 | <version>3.11.500-SNAPSHOT</version> | |
21 | 21 | <packaging>eclipse-plugin</packaging> |
22 | 22 | </project> |
82 | 82 | * @param editor the target editor |
83 | 83 | */ |
84 | 84 | public PredefinedEncodingAction(ResourceBundle bundle, String prefix, String encoding, ITextEditor editor) { |
85 | super(bundle, prefix, editor); | |
85 | super(bundle, prefix, editor); | |
86 | 86 | fEncoding= encoding; |
87 | 87 | if (prefix == null) |
88 | 88 | setText(encoding); |
97 | 97 | * @param editor the target editor |
98 | 98 | */ |
99 | 99 | public PredefinedEncodingAction(ResourceBundle bundle, String encoding, ITextEditor editor) { |
100 | super(bundle, null, editor); | |
100 | super(bundle, null, editor); | |
101 | 101 | fEncoding= encoding; |
102 | 102 | setText(encoding); |
103 | 103 | fLabel= getText(); |
672 | 672 | |
673 | 673 | IFileEditorInput input= (IFileEditorInput) element; |
674 | 674 | |
675 | try { | |
676 | refreshFile(input.getFile()); | |
677 | } catch (CoreException x) { | |
678 | handleCoreException(x, TextEditorMessages.FileDocumentProvider_createElementInfo); | |
675 | // Note that file.isSynchronized does not require a scheduling rule and thus helps to identify a no-op attempt | |
676 | // to refresh the file. The no-op will otherwise be blocked by a running build or cancel a running build | |
677 | IFile file= input.getFile(); | |
678 | if (!file.isSynchronized(IResource.DEPTH_ZERO)) { | |
679 | try { | |
680 | refreshFile(file); | |
681 | } catch (CoreException x) { | |
682 | handleCoreException(x, TextEditorMessages.FileDocumentProvider_createElementInfo); | |
683 | } | |
679 | 684 | } |
680 | 685 | |
681 | 686 | IDocument d= null; |
1127 | 1132 | protected ISchedulingRule getValidateStateRule(Object element) { |
1128 | 1133 | if (element instanceof IFileEditorInput) { |
1129 | 1134 | IFileEditorInput input= (IFileEditorInput) element; |
1130 | return fResourceRuleFactory.validateEditRule(new IResource[] { input.getFile() }); | |
1135 | IFile file= input.getFile(); | |
1136 | ISchedulingRule validateEditRule= fResourceRuleFactory.validateEditRule(new IResource[] { file }); | |
1137 | if (validateEditRule == null) { | |
1138 | // Note that factory decides to provide a null rule for modifiable files (not read-only). | |
1139 | // Null rule means, that org.eclipse.core.internal.resources.WorkManager.checkIn(ISchedulingRule, IProgressMonitor) | |
1140 | // will run jobManager.beginRule(null, monitor); which will NOT show any progress dialog | |
1141 | // and will *immediately* lock UI thread via lock.acquire(); while the workspace is locked | |
1142 | // Providing here a file we enforce the progress dialog, where this operation can be cancelled by user, | |
1143 | // so that an occasional "Modify" or "Save" of the editor will NOT block UI forever. | |
1144 | return file; | |
1145 | } else { | |
1146 | return validateEditRule; | |
1147 | } | |
1131 | 1148 | } |
1132 | 1149 | return null; |
1133 | 1150 | } |
96 | 96 | /** |
97 | 97 | * Installs the encoding support on the given text editor. |
98 | 98 | * <p> |
99 | * Subclasses may override to install their own encoding | |
100 | * support or to disable the default encoding support. | |
101 | * </p> | |
99 | * Subclasses may override to install their own encoding | |
100 | * support or to disable the default encoding support. | |
101 | * </p> | |
102 | 102 | * @since 2.1 |
103 | 103 | */ |
104 | 104 | protected void installEncodingSupport() { |
+7
-7
23 | 23 | * @since 2.1 |
24 | 24 | * @noinstantiate This class is not intended to be instantiated by clients. |
25 | 25 | * @noextend This class is not intended to be subclassed by clients. |
26 | */ | |
26 | */ | |
27 | 27 | public class TextEditorPreferenceConstants { |
28 | 28 | |
29 | 29 | /** |
59 | 59 | |
60 | 60 | /** |
61 | 61 | * A named preference that controls whether the print margin is turned on or off |
62 | * (value <code>"printMargin"</code>). | |
62 | * (value <code>"printMargin"</code>). | |
63 | 63 | * <p> |
64 | 64 | * The preference value is of type <code>Boolean</code>. |
65 | 65 | * </p> |
124 | 124 | |
125 | 125 | /** |
126 | 126 | * A named preference that controls whether the overview ruler shows unknown indicators |
127 | * (value <code>"othersIndicationInOverviewRuler"</code>). | |
127 | * (value <code>"othersIndicationInOverviewRuler"</code>). | |
128 | 128 | * <p> |
129 | 129 | * The preference value is of type <code>Boolean</code>. |
130 | 130 | * </p> |
136 | 136 | |
137 | 137 | /** |
138 | 138 | * A named preference that controls if the overview ruler is shown in the UI |
139 | * (value <code>"overviewRuler"</code>). | |
139 | * (value <code>"overviewRuler"</code>). | |
140 | 140 | * <p> |
141 | 141 | * The preference value is of type <code>Boolean</code>. |
142 | 142 | * </p> |
280 | 280 | public final static String EDITOR_SEARCH_RESULT_INDICATION_IN_OVERVIEW_RULER= "searchResultIndicationInOverviewRuler"; //$NON-NLS-1$ |
281 | 281 | |
282 | 282 | /** |
283 | * Initializes the given preference store with the default values. | |
283 | * Initializes the given preference store with the default values. | |
284 | 284 | * |
285 | * @param store the preference store to be initialized | |
286 | */ | |
285 | * @param store the preference store to be initialized | |
286 | */ | |
287 | 287 | public static void initializeDefaultValues(IPreferenceStore store) { |
288 | 288 | |
289 | 289 | // set defaults from AbstractDecoratedTextEditor |
+13
-1
1016 | 1016 | public ISchedulingRule getSchedulingRule() { |
1017 | 1017 | if (info.fElement instanceof IFileEditorInput) { |
1018 | 1018 | IFileEditorInput input= (IFileEditorInput) info.fElement; |
1019 | return fResourceRuleFactory.validateEditRule(new IResource[] { input.getFile() }); | |
1019 | IFile file= input.getFile(); | |
1020 | ISchedulingRule validateEditRule= fResourceRuleFactory.validateEditRule(new IResource[] { file }); | |
1021 | if (validateEditRule == null) { | |
1022 | // Note that factory decides to provide a null rule for modifiable files (not read-only). | |
1023 | // Null rule means, that org.eclipse.core.internal.resources.WorkManager.checkIn(ISchedulingRule, IProgressMonitor) | |
1024 | // will run jobManager.beginRule(null, monitor); which will NOT show any progress dialog | |
1025 | // and will *immediately* lock UI thread via lock.acquire(); while the workspace is locked | |
1026 | // Providing here a file we enforce the progress dialog, where this operation can be cancelled by user, | |
1027 | // so that an occasional "Modify" or "Save" of the editor will NOT block UI forever. | |
1028 | return file; | |
1029 | } else { | |
1030 | return validateEditRule; | |
1031 | } | |
1020 | 1032 | } |
1021 | 1033 | return null; |
1022 | 1034 | } |
+1
-1
89 | 89 | return filterUnacceptableFiles(files); |
90 | 90 | } |
91 | 91 | |
92 | final IFilter filter= new IFilter() { | |
92 | final IFilter filter= new IFilter() { | |
93 | 93 | @Override |
94 | 94 | public boolean accept(IResource resource) { |
95 | 95 | return resource != null && isAcceptableLocation(resource.getFullPath()); |
+25
-25
374 | 374 | * @param chars the number of characters |
375 | 375 | * @return the number of pixels |
376 | 376 | */ |
377 | protected int convertWidthInCharsToPixels(int chars) { | |
378 | // test for failure to initialize for backward compatibility | |
379 | if (fFontMetrics == null) | |
380 | return 0; | |
381 | return Dialog.convertWidthInCharsToPixels(fFontMetrics, chars); | |
382 | } | |
377 | protected int convertWidthInCharsToPixels(int chars) { | |
378 | // test for failure to initialize for backward compatibility | |
379 | if (fFontMetrics == null) | |
380 | return 0; | |
381 | return Dialog.convertWidthInCharsToPixels(fFontMetrics, chars); | |
382 | } | |
383 | 383 | |
384 | 384 | /** |
385 | 385 | * Returns the number of pixels corresponding to the height of the given number of characters. |
393 | 393 | * @param chars the number of characters |
394 | 394 | * @return the number of pixels |
395 | 395 | */ |
396 | protected int convertHeightInCharsToPixels(int chars) { | |
397 | // test for failure to initialize for backward compatibility | |
398 | if (fFontMetrics == null) | |
399 | return 0; | |
400 | return Dialog.convertHeightInCharsToPixels(fFontMetrics, chars); | |
401 | } | |
396 | protected int convertHeightInCharsToPixels(int chars) { | |
397 | // test for failure to initialize for backward compatibility | |
398 | if (fFontMetrics == null) | |
399 | return 0; | |
400 | return Dialog.convertHeightInCharsToPixels(fFontMetrics, chars); | |
401 | } | |
402 | 402 | |
403 | 403 | /** |
404 | 404 | * Initializes the computation of horizontal and vertical dialog units based on the size of |
409 | 409 | * |
410 | 410 | * @param testControl a control from which to obtain the current font |
411 | 411 | */ |
412 | protected void initializeDialogUnits(Control testControl) { | |
413 | // Compute and store a font metric | |
414 | GC gc = new GC(testControl); | |
415 | gc.setFont(JFaceResources.getDialogFont()); | |
416 | fFontMetrics = gc.getFontMetrics(); | |
417 | gc.dispose(); | |
418 | } | |
412 | protected void initializeDialogUnits(Control testControl) { | |
413 | // Compute and store a font metric | |
414 | GC gc = new GC(testControl); | |
415 | gc.setFont(JFaceResources.getDialogFont()); | |
416 | fFontMetrics = gc.getFontMetrics(); | |
417 | gc.dispose(); | |
418 | } | |
419 | 419 | |
420 | 420 | private void handleAnnotationListSelection() { |
421 | 421 | ListItem item= getSelectedItem(); |
544 | 544 | |
545 | 545 | private void initializeFields() { |
546 | 546 | |
547 | // Update slaves | |
548 | Iterator<SelectionListener> iter= fMasterSlaveListeners.iterator(); | |
549 | while (iter.hasNext()) { | |
550 | SelectionListener listener= iter.next(); | |
551 | listener.widgetSelected(null); | |
552 | } | |
547 | // Update slaves | |
548 | Iterator<SelectionListener> iter= fMasterSlaveListeners.iterator(); | |
549 | while (iter.hasNext()) { | |
550 | SelectionListener listener= iter.next(); | |
551 | listener.widgetSelected(null); | |
552 | } | |
553 | 553 | } |
554 | 554 | |
555 | 555 | @Override |
+3
-3
22 | 22 | */ |
23 | 23 | public class PreviousPulldownActionDelegate extends NextPreviousPulldownActionDelegate { |
24 | 24 | |
25 | @Override | |
25 | @Override | |
26 | 26 | public String getPreferenceKey(AnnotationPreference annotationPreference) { |
27 | return annotationPreference.getIsGoToPreviousNavigationTargetKey(); | |
28 | } | |
27 | return annotationPreference.getIsGoToPreviousNavigationTargetKey(); | |
28 | } | |
29 | 29 | |
30 | 30 | } |
+1
-1
64 | 64 | if (containsOnlyFiles(resources)) |
65 | 65 | return files; |
66 | 66 | |
67 | final IFilter filter= new IFilter() { | |
67 | final IFilter filter= new IFilter() { | |
68 | 68 | @Override |
69 | 69 | public boolean accept(IResource resource) { |
70 | 70 | return resource != null && isAcceptableLocation(resource.getFullPath()); |
+1
-1
77 | 77 | |
78 | 78 | void filterElements(Collection<Object> elements) throws InterruptedException; |
79 | 79 | |
80 | void filterElements(Object[] elements) throws InterruptedException; | |
80 | void filterElements(Object[] elements) throws InterruptedException; | |
81 | 81 | } |
82 | 82 | |
83 | 83 |
+55
-55
62 | 62 | } |
63 | 63 | |
64 | 64 | private SelectResourcesBlock fResourceGroup; |
65 | private List<Object> fAcceptedFileTypes = new ArrayList<>(); | |
66 | private IResource[] fInput; | |
67 | private String fTitle; | |
68 | private String fInstruction; | |
69 | private Label fCountIndication; | |
65 | private List<Object> fAcceptedFileTypes = new ArrayList<>(); | |
66 | private IResource[] fInput; | |
67 | private String fTitle; | |
68 | private String fInstruction; | |
69 | private Label fCountIndication; | |
70 | 70 | private IFilter fAcceptableLocationsFilter; |
71 | 71 | |
72 | 72 | |
129 | 129 | return (displayHeight / fontHeight) > 50; |
130 | 130 | } |
131 | 131 | |
132 | private ITreeContentProvider getResourceProvider(final int resourceType) { | |
133 | return new WorkbenchContentProvider() { | |
134 | @Override | |
132 | private ITreeContentProvider getResourceProvider(final int resourceType) { | |
133 | return new WorkbenchContentProvider() { | |
134 | @Override | |
135 | 135 | public Object[] getChildren(Object o) { |
136 | if (o instanceof IWorkspaceRoot) { | |
137 | HashSet<IResource> projects= new HashSet<>(); | |
138 | for (int i= 0; i < fInput.length; i++) { | |
139 | IResource project= fInput[i].getProject(); | |
140 | if ((project.getType() & resourceType) > 0) | |
141 | projects.add(project); | |
142 | } | |
143 | return projects.toArray(); | |
144 | } | |
145 | ||
146 | if (o instanceof IContainer) { | |
147 | IResource[] members = null; | |
148 | try { | |
149 | members = ((IContainer) o).members(); | |
150 | } catch (CoreException e) { | |
151 | //just return an empty set of children | |
152 | return new Object[0]; | |
153 | } | |
154 | ||
155 | //filter out the desired resource types | |
156 | ArrayList<IResource> results = new ArrayList<>(); | |
157 | for (int i = 0; i < members.length; i++) { | |
158 | //And the test bits with the resource types to see if they are what we want | |
159 | if ((members[i].getType() & resourceType) > 0 && (resourceType != IResource.FILE || fAcceptableLocationsFilter == null || fAcceptableLocationsFilter.accept(members[i]))) { | |
160 | results.add(members[i]); | |
161 | } | |
162 | } | |
163 | return results.toArray(); | |
164 | } | |
165 | ||
166 | //input element case | |
167 | if (o instanceof ArrayList) | |
168 | return ((ArrayList<?>) o).toArray(); | |
169 | ||
170 | return new Object[0]; | |
171 | } | |
172 | }; | |
173 | } | |
174 | ||
175 | protected Composite createSelectionButtonGroup(Composite parent) { | |
136 | if (o instanceof IWorkspaceRoot) { | |
137 | HashSet<IResource> projects= new HashSet<>(); | |
138 | for (int i= 0; i < fInput.length; i++) { | |
139 | IResource project= fInput[i].getProject(); | |
140 | if ((project.getType() & resourceType) > 0) | |
141 | projects.add(project); | |
142 | } | |
143 | return projects.toArray(); | |
144 | } | |
145 | ||
146 | if (o instanceof IContainer) { | |
147 | IResource[] members = null; | |
148 | try { | |
149 | members = ((IContainer) o).members(); | |
150 | } catch (CoreException e) { | |
151 | //just return an empty set of children | |
152 | return new Object[0]; | |
153 | } | |
154 | ||
155 | //filter out the desired resource types | |
156 | ArrayList<IResource> results = new ArrayList<>(); | |
157 | for (int i = 0; i < members.length; i++) { | |
158 | //And the test bits with the resource types to see if they are what we want | |
159 | if ((members[i].getType() & resourceType) > 0 && (resourceType != IResource.FILE || fAcceptableLocationsFilter == null || fAcceptableLocationsFilter.accept(members[i]))) { | |
160 | results.add(members[i]); | |
161 | } | |
162 | } | |
163 | return results.toArray(); | |
164 | } | |
165 | ||
166 | //input element case | |
167 | if (o instanceof ArrayList) | |
168 | return ((ArrayList<?>) o).toArray(); | |
169 | ||
170 | return new Object[0]; | |
171 | } | |
172 | }; | |
173 | } | |
174 | ||
175 | protected Composite createSelectionButtonGroup(Composite parent) { | |
176 | 176 | |
177 | 177 | Font font= parent.getFont(); |
178 | 178 | |
224 | 224 | return buttonComposite; |
225 | 225 | } |
226 | 226 | |
227 | protected void handleSelectFileTypes() { | |
227 | protected void handleSelectFileTypes() { | |
228 | 228 | Object[] acceptedFileTypes= queryFileTypes(); |
229 | 229 | if (acceptedFileTypes != null) { |
230 | 230 | fAcceptedFileTypes= Arrays.asList(acceptedFileTypes); |
232 | 232 | } |
233 | 233 | } |
234 | 234 | |
235 | protected Object[] queryFileTypes() { | |
235 | protected Object[] queryFileTypes() { | |
236 | 236 | TypeFilteringDialog dialog= new TypeFilteringDialog(getShell(), fAcceptedFileTypes); |
237 | 237 | dialog.open(); |
238 | 238 | return dialog.getResult(); |
239 | 239 | } |
240 | 240 | |
241 | private void filterSelection() { | |
241 | private void filterSelection() { | |
242 | 242 | |
243 | 243 | final IFilter filter= resource -> hasAcceptedFileType(resource); |
244 | 244 | |
247 | 247 | |
248 | 248 | Runnable runnable= () -> setSelection(resources, filter); |
249 | 249 | |
250 | BusyIndicator.showWhile(getShell().getDisplay(), runnable); | |
251 | } | |
252 | ||
253 | protected boolean hasAcceptedFileType(IResource resource) { | |
250 | BusyIndicator.showWhile(getShell().getDisplay(), runnable); | |
251 | } | |
252 | ||
253 | protected boolean hasAcceptedFileType(IResource resource) { | |
254 | 254 | if (fAcceptedFileTypes == null) |
255 | 255 | return true; |
256 | 256 |
+6
-6
1057 | 1057 | fFieldsInitialized= true; |
1058 | 1058 | updateStatus(new StatusInfo()); |
1059 | 1059 | |
1060 | // Update slaves | |
1061 | Iterator<SelectionListener> iter= fMasterSlaveListeners.iterator(); | |
1062 | while (iter.hasNext()) { | |
1063 | SelectionListener listener= iter.next(); | |
1064 | listener.widgetSelected(null); | |
1065 | } | |
1060 | // Update slaves | |
1061 | Iterator<SelectionListener> iter= fMasterSlaveListeners.iterator(); | |
1062 | while (iter.hasNext()) { | |
1063 | SelectionListener listener= iter.next(); | |
1064 | listener.widgetSelected(null); | |
1065 | } | |
1066 | 1066 | |
1067 | 1067 | } |
1068 | 1068 |
+13
-13
1184 | 1184 | setAction(ITextEditorActionConstants.QUICKDIFF_REVERTDELETION, action); |
1185 | 1185 | |
1186 | 1186 | IAction action2= new CompositeRevertAction(this, new IAction[] { |
1187 | getAction(ITextEditorActionConstants.QUICKDIFF_REVERTSELECTION), | |
1188 | getAction(ITextEditorActionConstants.QUICKDIFF_REVERTBLOCK), | |
1189 | getAction(ITextEditorActionConstants.QUICKDIFF_REVERTDELETION), | |
1190 | getAction(ITextEditorActionConstants.QUICKDIFF_REVERTLINE)}); | |
1187 | getAction(ITextEditorActionConstants.QUICKDIFF_REVERTSELECTION), | |
1188 | getAction(ITextEditorActionConstants.QUICKDIFF_REVERTBLOCK), | |
1189 | getAction(ITextEditorActionConstants.QUICKDIFF_REVERTDELETION), | |
1190 | getAction(ITextEditorActionConstants.QUICKDIFF_REVERTLINE)}); | |
1191 | 1191 | action2.setActionDefinitionId(ITextEditorActionDefinitionIds.QUICKDIFF_REVERT); |
1192 | 1192 | setAction(ITextEditorActionConstants.QUICKDIFF_REVERT, action2); |
1193 | 1193 | |
1309 | 1309 | |
1310 | 1310 | IAnnotationHover hover= fLineColumn.getHover(); |
1311 | 1311 | showFocusedRulerHover(hover, sourceViewer, caretOffset); |
1312 | } | |
1312 | } | |
1313 | 1313 | |
1314 | 1314 | /** |
1315 | 1315 | * Opens a sticky annotation ruler hover for the caret line. Does nothing if no annotation hover |
1527 | 1527 | // Check whether file exists and if so, confirm overwrite |
1528 | 1528 | final File localFile= new File(path); |
1529 | 1529 | if (localFile.exists()) { |
1530 | MessageDialog overwriteDialog= new MessageDialog( | |
1531 | shell, | |
1532 | TextEditorMessages.AbstractDecoratedTextEditor_saveAs_overwrite_title, | |
1533 | null, | |
1534 | NLSUtility.format(TextEditorMessages.AbstractDecoratedTextEditor_saveAs_overwrite_message, path), | |
1535 | MessageDialog.WARNING, | |
1536 | new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, | |
1537 | 1); // 'No' is the default | |
1530 | MessageDialog overwriteDialog= new MessageDialog( | |
1531 | shell, | |
1532 | TextEditorMessages.AbstractDecoratedTextEditor_saveAs_overwrite_title, | |
1533 | null, | |
1534 | NLSUtility.format(TextEditorMessages.AbstractDecoratedTextEditor_saveAs_overwrite_message, path), | |
1535 | MessageDialog.WARNING, | |
1536 | new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL }, | |
1537 | 1); // 'No' is the default | |
1538 | 1538 | if (overwriteDialog.open() != Window.OK) { |
1539 | 1539 | if (progressMonitor != null) { |
1540 | 1540 | progressMonitor.setCanceled(true); |
+7
-7
101 | 101 | |
102 | 102 | /** |
103 | 103 | * A named preference that controls whether the print margin is turned on or off |
104 | * (value <code>"printMargin"</code>). | |
104 | * (value <code>"printMargin"</code>). | |
105 | 105 | * <p> |
106 | 106 | * The preference value is of type <code>Boolean</code>. |
107 | 107 | * </p> |
169 | 169 | |
170 | 170 | /** |
171 | 171 | * A named preference that controls whether the overview ruler shows unknown indicators |
172 | * (value <code>"othersIndicationInOverviewRuler"</code>). | |
172 | * (value <code>"othersIndicationInOverviewRuler"</code>). | |
173 | 173 | * <p> |
174 | 174 | * The preference value is of type <code>Boolean</code>. |
175 | 175 | * </p> |
180 | 180 | |
181 | 181 | /** |
182 | 182 | * A named preference that controls if the overview ruler is shown in the UI |
183 | * (value <code>"overviewRuler"</code>). | |
183 | * (value <code>"overviewRuler"</code>). | |
184 | 184 | * <p> |
185 | 185 | * The preference value is of type <code>Boolean</code>. |
186 | 186 | * </p> |
671 | 671 | public static final String EDITOR_HOVER_ENRICH_MODE= AbstractTextEditor.PREFERENCE_HOVER_ENRICH_MODE; |
672 | 672 | |
673 | 673 | /** |
674 | * Initializes the given preference store with the default values. | |
675 | * | |
676 | * @param store the preference store to be initialized | |
677 | */ | |
674 | * Initializes the given preference store with the default values. | |
675 | * | |
676 | * @param store the preference store to be initialized | |
677 | */ | |
678 | 678 | public static void initializeDefaultValues(IPreferenceStore store) { |
679 | 679 | store.setDefault(AbstractDecoratedTextEditorPreferenceConstants.USE_ANNOTATIONS_PREFERENCE_PAGE, false); |
680 | 680 | store.setDefault(AbstractDecoratedTextEditorPreferenceConstants.USE_QUICK_DIFF_PREFERENCE_PAGE, false); |
62 | 62 | if (resource == null) |
63 | 63 | return; |
64 | 64 | |
65 | TaskPropertiesDialog dialog = new TaskPropertiesDialog(getTextEditor().getSite().getShell()); | |
66 | dialog.setResource(resource); | |
67 | dialog.setInitialAttributes(getInitialAttributes()); | |
68 | dialog.open(); | |
65 | TaskPropertiesDialog dialog = new TaskPropertiesDialog(getTextEditor().getSite().getShell()); | |
66 | dialog.setResource(resource); | |
67 | dialog.setInitialAttributes(getInitialAttributes()); | |
68 | dialog.open(); | |
69 | 69 | } |
70 | 70 | } |
71 | 71 |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %Plugin.name |
3 | 3 | Bundle-SymbolicName: org.eclipse.ui.editors.tests;singleton:=true |
4 | Bundle-Version: 3.11.300.qualifier | |
4 | Bundle-Version: 3.11.400.qualifier | |
5 | 5 | Bundle-Vendor: %Plugin.providerName |
6 | 6 | Bundle-Localization: plugin |
7 | 7 | Export-Package: org.eclipse.ui.editors.tests |
13 | 13 | <parent> |
14 | 14 | <artifactId>tests-pom</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | <relativePath>../tests-pom/</relativePath> |
18 | 18 | </parent> |
19 | 19 | <groupId>org.eclipse.ui</groupId> |
20 | 20 | <artifactId>org.eclipse.ui.editors.tests</artifactId> |
21 | <version>3.11.300-SNAPSHOT</version> | |
21 | <version>3.11.400-SNAPSHOT</version> | |
22 | 22 | <packaging>eclipse-test-plugin</packaging> |
23 | 23 | <properties> |
24 | 24 | <testSuite>${project.artifactId}</testSuite> |
32 | 32 | MarkerAnnotationOrderTest.class, |
33 | 33 | ZoomTest.class, |
34 | 34 | FileDocumentProviderTest.class, |
35 | TextFileDocumentProviderTest.class, | |
35 | 36 | StatusEditorTest.class |
36 | 37 | }) |
37 | 38 | public class EditorsTestSuite { |
+124
-5
29 | 29 | |
30 | 30 | import org.eclipse.core.internal.localstore.FileSystemResourceManager; |
31 | 31 | import org.eclipse.core.internal.resources.File; |
32 | import org.eclipse.core.internal.resources.Workspace; | |
32 | 33 | |
33 | 34 | import org.eclipse.core.runtime.CoreException; |
34 | 35 | import org.eclipse.core.runtime.ILog; |
55 | 56 | import org.eclipse.ui.IEditorPart; |
56 | 57 | import org.eclipse.ui.IWorkbench; |
57 | 58 | import org.eclipse.ui.IWorkbenchPage; |
59 | import org.eclipse.ui.IWorkbenchWindow; | |
58 | 60 | import org.eclipse.ui.PlatformUI; |
59 | 61 | import org.eclipse.ui.ide.IDE; |
60 | 62 | import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor; |
63 | import org.eclipse.ui.intro.IIntroManager; | |
64 | import org.eclipse.ui.intro.IIntroPart; | |
61 | 65 | |
62 | 66 | import org.eclipse.ui.editors.text.FileDocumentProvider; |
63 | 67 | |
73 | 77 | private AtomicBoolean stoppedByTest; |
74 | 78 | private AtomicBoolean stopLockingFlag; |
75 | 79 | private LockJob lockJob; |
80 | private LockJob2 lockJob2; | |
76 | 81 | private FileDocumentProviderMock fileProvider; |
77 | 82 | private FileSystemResourceManager fsManager; |
78 | 83 | private IEditorPart editor; |
80 | 85 | |
81 | 86 | @Before |
82 | 87 | public void setUp() throws Exception { |
88 | closeIntro(PlatformUI.getWorkbench()); | |
83 | 89 | IFolder folder = ResourceHelper.createFolder("FileDocumentProviderTestProject/test"); |
84 | 90 | file = (File) ResourceHelper.createFile(folder, "file.txt", ""); |
85 | 91 | assertTrue(file.exists()); |
89 | 95 | stoppedByTest = new AtomicBoolean(false); |
90 | 96 | fileProvider = new FileDocumentProviderMock(); |
91 | 97 | lockJob = new LockJob("Locking workspace", file, stopLockingFlag, stoppedByTest); |
98 | lockJob2 = new LockJob2("Locking workspace", file, stopLockingFlag, stoppedByTest); | |
92 | 99 | |
93 | 100 | // We need the editor only to get the default editor status line manager |
94 | 101 | IWorkbench workbench = PlatformUI.getWorkbench(); |
125 | 132 | public void tearDown() throws Exception { |
126 | 133 | stopLockingFlag.set(true); |
127 | 134 | lockJob.cancel(); |
135 | lockJob2.cancel(); | |
128 | 136 | if (editor != null) { |
129 | 137 | page.closeEditor(editor, false); |
130 | 138 | } |
194 | 202 | |
195 | 203 | System.out.println("Busy wait terminated, UI thread is operable again!"); |
196 | 204 | |
205 | assertFalse("Test deadlocked while waiting on resource lock", stoppedByTest.get()); | |
206 | assertTrue(stopLockingFlag.get()); | |
207 | } | |
208 | ||
209 | @Test | |
210 | public void testValidateStateForFileWhileWorkspaceIsLocked() throws Exception { | |
211 | assertNotNull("Test must run in UI thread", Display.getCurrent()); | |
212 | ||
213 | // Start workspace job which will lock workspace operations on file | |
214 | lockJob2.schedule(); | |
215 | ||
216 | Thread.sleep(100); | |
217 | ||
218 | // Put an UI event in the queue which will stop the workspace lock job | |
219 | // after a delay | |
220 | Display.getCurrent().timerExec(600, new Runnable() { | |
221 | @Override | |
222 | public void run() { | |
223 | stopLockingFlag.set(true); | |
224 | System.out.println("UI event dispatched, lock removed"); | |
225 | } | |
226 | }); | |
227 | ||
228 | // Original code will lock UI thread here because it will try to acquire | |
229 | // workspace lock and no one will process UI events anymore | |
230 | fileProvider.validateState(editor.getEditorInput(), editor.getSite().getShell()); | |
231 | ||
232 | System.out.println("Busy wait terminated, UI thread is operable again!"); | |
197 | 233 | assertFalse("Test deadlocked while waiting on resource lock", stoppedByTest.get()); |
198 | 234 | assertTrue(stopLockingFlag.get()); |
199 | 235 | } |
226 | 262 | ILog log = Platform.getLog(Platform.getBundle(PLUGIN_ID)); |
227 | 263 | log.log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, message, ex)); |
228 | 264 | } |
265 | ||
266 | static void closeIntro(final IWorkbench wb) { | |
267 | IWorkbenchWindow window = wb.getActiveWorkbenchWindow(); | |
268 | if (window != null) { | |
269 | IIntroManager im = wb.getIntroManager(); | |
270 | IIntroPart intro = im.getIntro(); | |
271 | if (intro != null) { | |
272 | im.closeIntro(intro); | |
273 | } | |
274 | } | |
275 | } | |
229 | 276 | } |
230 | 277 | |
231 | 278 | class FileDocumentProviderMock extends FileDocumentProvider { |
252 | 299 | /** Emulates what SVN plugin jobs are doing */ |
253 | 300 | class LockJob extends WorkspaceJob { |
254 | 301 | |
255 | private final IResource resource; | |
256 | private AtomicBoolean stopFlag; | |
257 | private AtomicBoolean stoppedByTest; | |
302 | final IResource resource; | |
303 | AtomicBoolean stopFlag; | |
304 | AtomicBoolean stoppedByTest; | |
258 | 305 | |
259 | 306 | public LockJob(String name, IResource resource, AtomicBoolean stopFlag, AtomicBoolean stoppedByTest) { |
260 | 307 | super(name); |
265 | 312 | this.resource = resource; |
266 | 313 | } |
267 | 314 | |
268 | public IStatus run2(IProgressMonitor monitor) { | |
315 | public IStatus run2() { | |
269 | 316 | long startTime = System.currentTimeMillis(); |
270 | 317 | // Wait maximum 5 minutes |
271 | 318 | int maxWaitTime = 5 * 60 * 1000; |
311 | 358 | @Override |
312 | 359 | public void run(IProgressMonitor pm) throws CoreException { |
313 | 360 | try { |
314 | run2(pm); | |
361 | run2(); | |
315 | 362 | } catch (Exception e) { |
316 | 363 | // Re-throw as OperationCanceledException, which will be |
317 | 364 | // caught and re-thrown as InterruptedException below. |
327 | 374 | } |
328 | 375 | |
329 | 376 | } |
377 | ||
378 | /** Emulates what AutoBuildJob is doing */ | |
379 | class LockJob2 extends Job { | |
380 | ||
381 | final IResource resource; | |
382 | AtomicBoolean stopFlag; | |
383 | AtomicBoolean stoppedByTest; | |
384 | ||
385 | public LockJob2(String name, IResource resource, AtomicBoolean stopFlag, AtomicBoolean stoppedByTest) { | |
386 | super(name); | |
387 | this.stopFlag = stopFlag; | |
388 | this.stoppedByTest = stoppedByTest; | |
389 | setUser(true); | |
390 | setSystem(true); | |
391 | this.resource = resource; | |
392 | setRule(ResourcesPlugin.getWorkspace().getRoot()); | |
393 | } | |
394 | ||
395 | @Override | |
396 | public boolean belongsTo(Object family) { | |
397 | return super.belongsTo(family) || LockJob2.class == family; | |
398 | } | |
399 | ||
400 | @Override | |
401 | public IStatus run(IProgressMonitor monitor) { | |
402 | Workspace workspace = (Workspace) ResourcesPlugin.getWorkspace(); | |
403 | try { | |
404 | workspace.prepareOperation(getRule(), monitor); | |
405 | workspace.beginOperation(true); | |
406 | run2(); | |
407 | } catch (CoreException e) { | |
408 | FileDocumentProviderTest.logError(e.getMessage(), e); | |
409 | } finally { | |
410 | try { | |
411 | workspace.endOperation(getRule(), false); | |
412 | } catch (CoreException e) { | |
413 | FileDocumentProviderTest.logError(e.getMessage(), e); | |
414 | } | |
415 | } | |
416 | return monitor.isCanceled() ? Status.CANCEL_STATUS : Status.OK_STATUS; | |
417 | } | |
418 | ||
419 | public IStatus run2() { | |
420 | long startTime = System.currentTimeMillis(); | |
421 | // Wait maximum 5 minutes | |
422 | int maxWaitTime = 5 * 60 * 1000; | |
423 | long stopTime = startTime + maxWaitTime; | |
424 | ||
425 | System.out.println("Starting the busy wait while holding lock on: " + resource.getFullPath()); | |
426 | try { | |
427 | while (!stopFlag.get()) { | |
428 | try { | |
429 | if (System.currentTimeMillis() > stopTime) { | |
430 | FileDocumentProviderTest.logError("Tiemout occured while waiting on test to finish", | |
431 | new IllegalStateException()); | |
432 | stoppedByTest.set(true); | |
433 | return Status.CANCEL_STATUS; | |
434 | } | |
435 | Thread.sleep(100); | |
436 | } catch (InterruptedException e) { | |
437 | stoppedByTest.set(true); | |
438 | FileDocumentProviderTest.logError("Lock job was interrupted while waiting on test to finish", e); | |
439 | e.printStackTrace(); | |
440 | return Status.CANCEL_STATUS; | |
441 | } | |
442 | } | |
443 | } finally { | |
444 | System.out.println("Lock task terminated"); | |
445 | } | |
446 | return Status.OK_STATUS; | |
447 | } | |
448 | } |
+251
-0
0 | /******************************************************************************* | |
1 | * Copyright (c) 2016 Andrey Loskutov. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Andrey Loskutov <loskutov@gmx.de> - initial API and implementation | |
12 | *******************************************************************************/ | |
13 | package org.eclipse.ui.editors.tests; | |
14 | ||
15 | import static org.eclipse.ui.editors.tests.FileDocumentProviderTest.closeIntro; | |
16 | import static org.junit.Assert.assertFalse; | |
17 | import static org.junit.Assert.assertNotNull; | |
18 | import static org.junit.Assert.assertTrue; | |
19 | ||
20 | import java.nio.file.Files; | |
21 | import java.nio.file.attribute.FileTime; | |
22 | import java.util.Arrays; | |
23 | import java.util.concurrent.atomic.AtomicBoolean; | |
24 | ||
25 | import org.junit.After; | |
26 | import org.junit.Before; | |
27 | import org.junit.Test; | |
28 | ||
29 | import org.eclipse.swt.widgets.Display; | |
30 | ||
31 | import org.eclipse.core.internal.localstore.FileSystemResourceManager; | |
32 | import org.eclipse.core.internal.resources.File; | |
33 | ||
34 | import org.eclipse.core.runtime.IProgressMonitor; | |
35 | import org.eclipse.core.runtime.IProgressMonitorWithBlocking; | |
36 | import org.eclipse.core.runtime.NullProgressMonitor; | |
37 | import org.eclipse.core.runtime.jobs.Job; | |
38 | ||
39 | import org.eclipse.core.resources.IFolder; | |
40 | ||
41 | import org.eclipse.core.filebuffers.tests.ResourceHelper; | |
42 | ||
43 | import org.eclipse.jface.action.IStatusLineManager; | |
44 | ||
45 | import org.eclipse.ui.IEditorPart; | |
46 | import org.eclipse.ui.IWorkbench; | |
47 | import org.eclipse.ui.IWorkbenchPage; | |
48 | import org.eclipse.ui.PlatformUI; | |
49 | import org.eclipse.ui.ide.IDE; | |
50 | import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor; | |
51 | ||
52 | import org.eclipse.ui.editors.text.TextFileDocumentProvider; | |
53 | ||
54 | /** | |
55 | * Test checking UI deadlock on modifying the TextFileDocumentProvider's | |
56 | * underlined file. | |
57 | */ | |
58 | public class TextFileDocumentProviderTest { | |
59 | ||
60 | private File file; | |
61 | private AtomicBoolean stoppedByTest; | |
62 | private AtomicBoolean stopLockingFlag; | |
63 | private LockJob lockJob; | |
64 | private LockJob2 lockJob2; | |
65 | private TextFileDocumentProvider fileProvider; | |
66 | private FileSystemResourceManager fsManager; | |
67 | private IEditorPart editor; | |
68 | private IWorkbenchPage page; | |
69 | ||
70 | @Before | |
71 | public void setUp() throws Exception { | |
72 | closeIntro(PlatformUI.getWorkbench()); | |
73 | IFolder folder = ResourceHelper.createFolder("FileDocumentProviderTestProject/test"); | |
74 | file = (File) ResourceHelper.createFile(folder, "file.txt", ""); | |
75 | assertTrue(file.exists()); | |
76 | fsManager = file.getLocalManager(); | |
77 | assertTrue(fsManager.fastIsSynchronized(file)); | |
78 | stopLockingFlag = new AtomicBoolean(false); | |
79 | stoppedByTest = new AtomicBoolean(false); | |
80 | fileProvider = new TextFileDocumentProvider(); | |
81 | lockJob = new LockJob("Locking workspace", file, stopLockingFlag, stoppedByTest); | |
82 | lockJob2 = new LockJob2("Locking workspace", file, stopLockingFlag, stoppedByTest); | |
83 | ||
84 | // We need the editor only to get the default editor status line manager | |
85 | IWorkbench workbench = PlatformUI.getWorkbench(); | |
86 | page = workbench.getActiveWorkbenchWindow().getActivePage(); | |
87 | editor = IDE.openEditor(page, file); | |
88 | TestUtil.runEventLoop(); | |
89 | ||
90 | IStatusLineManager statusLineManager = editor.getEditorSite().getActionBars().getStatusLineManager(); | |
91 | // This is default monitor which almost all editors are using | |
92 | IProgressMonitor progressMonitor = statusLineManager.getProgressMonitor(); | |
93 | assertNotNull(progressMonitor); | |
94 | assertFalse(progressMonitor instanceof NullProgressMonitor); | |
95 | assertFalse(progressMonitor instanceof EventLoopProgressMonitor); | |
96 | assertTrue(progressMonitor instanceof IProgressMonitorWithBlocking); | |
97 | ||
98 | // Because this monitor is not EventLoopProgressMonitor, it will not | |
99 | // process UI events while waiting on workspace lock | |
100 | fileProvider.setProgressMonitor(progressMonitor); | |
101 | ||
102 | TestUtil.waitForJobs(500, 5000); | |
103 | Job[] jobs = Job.getJobManager().find(null); | |
104 | String jobsList = Arrays.toString(jobs); | |
105 | System.out.println("Still running jobs: " + jobsList); | |
106 | if (!Job.getJobManager().isIdle()) { | |
107 | jobs = Job.getJobManager().find(null); | |
108 | for (Job job : jobs) { | |
109 | System.out.println("Going to cancel: " + job.getName() + " / " + job); | |
110 | job.cancel(); | |
111 | } | |
112 | } | |
113 | } | |
114 | ||
115 | @After | |
116 | public void tearDown() throws Exception { | |
117 | stopLockingFlag.set(true); | |
118 | lockJob.cancel(); | |
119 | lockJob2.cancel(); | |
120 | if (editor != null) { | |
121 | page.closeEditor(editor, false); | |
122 | } | |
123 | ResourceHelper.deleteProject(file.getProject().getName()); | |
124 | TestUtil.runEventLoop(); | |
125 | TestUtil.cleanUp(); | |
126 | } | |
127 | ||
128 | @Test | |
129 | public void testSynchronizeInputWhileWorkspaceIsLocked1() throws Exception { | |
130 | // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=482354 | |
131 | assertNotNull("Test must run in UI thread", Display.getCurrent()); | |
132 | fileProvider.connect(editor.getEditorInput()); | |
133 | ||
134 | // Start workspace job which will lock workspace operations on file via | |
135 | // rule | |
136 | lockJob.schedule(); | |
137 | ||
138 | // touch the file of the editor | |
139 | makeSureResourceIsOutOfDate(); | |
140 | ||
141 | // Put an UI event in the queue which will stop the workspace lock job | |
142 | // after a delay so that we can verify the UI events are still | |
143 | // dispatched after the call to refreshFile() below | |
144 | Display.getCurrent().timerExec(500, new Runnable() { | |
145 | @Override | |
146 | public void run() { | |
147 | stopLockingFlag.set(true); | |
148 | System.out.println("UI event dispatched, lock removed"); | |
149 | } | |
150 | }); | |
151 | ||
152 | // Original code will lock UI thread here because it will try to acquire | |
153 | // resource lock and no one will process UI events anymore | |
154 | fileProvider.synchronize(editor.getEditorInput()); | |
155 | ||
156 | System.out.println("Busy wait terminated, UI thread is operable again!"); | |
157 | assertFalse("Test deadlocked while waiting on resource lock", stoppedByTest.get()); | |
158 | assertTrue(stopLockingFlag.get()); | |
159 | } | |
160 | ||
161 | @Test | |
162 | public void testSynchronizeInputWhileWorkspaceIsLocked2() throws Exception { | |
163 | // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=482354 | |
164 | assertNotNull("Test must run in UI thread", Display.getCurrent()); | |
165 | ||
166 | fileProvider.connect(editor.getEditorInput()); | |
167 | ||
168 | // Start workspace job which will lock workspace operations on file via | |
169 | // rule | |
170 | lockJob.schedule(); | |
171 | ||
172 | // touch the file of the editor | |
173 | makeSureResourceIsOutOfDate(); | |
174 | ||
175 | // Put an UI event in the queue which will stop the workspace lock job | |
176 | // after a delay so that we can verify the UI events are still | |
177 | // dispatched after the call to refreshFile() below | |
178 | Display.getCurrent().timerExec(500, new Runnable() { | |
179 | @Override | |
180 | public void run() { | |
181 | stopLockingFlag.set(true); | |
182 | System.out.println("UI event dispatched, lock removed"); | |
183 | } | |
184 | }); | |
185 | ||
186 | // Original code will lock UI thread here because it will try to acquire | |
187 | // resource lock and no one will process UI events anymore | |
188 | fileProvider.synchronize(editor.getEditorInput()); | |
189 | ||
190 | System.out.println("Busy wait terminated, UI thread is operable again!"); | |
191 | assertFalse("Test deadlocked while waiting on resource lock", stoppedByTest.get()); | |
192 | assertTrue(stopLockingFlag.get()); | |
193 | } | |
194 | ||
195 | @Test | |
196 | public void testValidateStateForFileWhileWorkspaceIsLocked() throws Exception { | |
197 | assertNotNull("Test must run in UI thread", Display.getCurrent()); | |
198 | ||
199 | fileProvider.connect(editor.getEditorInput()); | |
200 | ||
201 | // Start workspace job which will lock workspace operations on file | |
202 | lockJob2.schedule(); | |
203 | ||
204 | Thread.sleep(100); | |
205 | ||
206 | // Put an UI event in the queue which will stop the workspace lock job | |
207 | // after a delay | |
208 | Display.getCurrent().timerExec(600, new Runnable() { | |
209 | @Override | |
210 | public void run() { | |
211 | stopLockingFlag.set(true); | |
212 | System.out.println("UI event dispatched, lock removed"); | |
213 | } | |
214 | }); | |
215 | ||
216 | // Original code will lock UI thread here because it will try to acquire | |
217 | // workspace lock and no one will process UI events anymore | |
218 | fileProvider.validateState(editor.getEditorInput(), editor.getSite().getShell()); | |
219 | ||
220 | System.out.println("Busy wait terminated, UI thread is operable again!"); | |
221 | assertFalse("Test deadlocked while waiting on resource lock", stoppedByTest.get()); | |
222 | assertTrue(stopLockingFlag.get()); | |
223 | } | |
224 | ||
225 | /* | |
226 | * Set current time stamp via java.nio to make sure | |
227 | * org.eclipse.core.internal.resources.File.refreshLocal(int, | |
228 | * IProgressMonitor) will call super.refreshLocal(IResource.DEPTH_ZERO, | |
229 | * monitor) and so lock the UI by trying to access resource locked by the | |
230 | * job | |
231 | */ | |
232 | private void makeSureResourceIsOutOfDate() throws Exception { | |
233 | int count = 0; | |
234 | Files.setLastModifiedTime(file.getLocation().toFile().toPath(), | |
235 | FileTime.fromMillis(System.currentTimeMillis())); | |
236 | // Give the file system a chance to have a *different* timestamp | |
237 | Thread.sleep(100); | |
238 | while (fsManager.fastIsSynchronized(file) && count < 1000) { | |
239 | Files.setLastModifiedTime(file.getLocation().toFile().toPath(), | |
240 | FileTime.fromMillis(System.currentTimeMillis())); | |
241 | Thread.sleep(10); | |
242 | count++; | |
243 | } | |
244 | System.out.println("Managed to update file after " + count + " attempts"); | |
245 | assertFalse(fsManager.fastIsSynchronized(file)); | |
246 | } | |
247 | ||
248 | } | |
249 | ||
250 |
13 | 13 | <parent> |
14 | 14 | <artifactId>eclipse.platform.text</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | </parent> |
18 | 18 | <groupId>org.eclipse.ui</groupId> |
19 | 19 | <artifactId>org.eclipse.ui.examples.javaeditor</artifactId> |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %Bundle-Name |
3 | 3 | Bundle-SymbolicName: org.eclipse.ui.genericeditor;singleton:=true |
4 | Bundle-Version: 1.1.300.qualifier | |
4 | Bundle-Version: 1.1.400.qualifier | |
5 | 5 | Bundle-Vendor: %Bundle-Vendor |
6 | 6 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
7 | 7 | Require-Bundle: org.eclipse.ui.workbench.texteditor;bundle-version="3.10.0", |
13 | 13 | org.eclipse.jface;bundle-version="3.12.0", |
14 | 14 | org.eclipse.ui.ide;bundle-version="3.12.0", |
15 | 15 | org.eclipse.core.resources;bundle-version="3.11.0", |
16 | org.eclipse.core.expressions;bundle-version="3.6.0" | |
16 | org.eclipse.core.expressions;bundle-version="3.6.0", | |
17 | org.eclipse.compare;resolution:=optional | |
17 | 18 | Export-Package: org.eclipse.ui.internal.genericeditor;x-internal:=true, |
18 | 19 | org.eclipse.ui.internal.genericeditor.hover;x-internal:=true, |
19 | 20 | org.eclipse.ui.internal.genericeditor.markers;x-internal:=true, |
20 | org.eclipse.ui.internal.genericeditor.preferences;x-internal:=true | |
21 | org.eclipse.ui.internal.genericeditor.preferences;x-internal:=true, | |
22 | org.eclipse.ui.internal.genericeditor.compare;x-internal:=true | |
21 | 23 | Bundle-Activator: org.eclipse.ui.internal.genericeditor.GenericEditorPlugin |
22 | 24 | Bundle-Localization: plugin |
23 | 25 | Bundle-ActivationPolicy: lazy |
208 | 208 | <extension point="org.eclipse.core.runtime.preferences"> |
209 | 209 | <initializer class="org.eclipse.ui.internal.genericeditor.preferences.GenericEditorPluginPreferenceInitializer"/> |
210 | 210 | </extension> |
211 | <extension | |
212 | point="org.eclipse.compare.contentViewers"> | |
213 | <viewer | |
214 | class="org.eclipse.ui.internal.genericeditor.compare.CompareViewerCreator" | |
215 | id="org.eclipse.ui.genericeditor.compareViewer"> | |
216 | </viewer> | |
217 | </extension> | |
218 | <extension | |
219 | point="org.eclipse.compare.contentMergeViewers"> | |
220 | <viewer | |
221 | class="org.eclipse.ui.internal.genericeditor.compare.CompareViewerCreator" | |
222 | id="org.eclipse.ui.genericeditor.compareViewer"> | |
223 | </viewer> | |
224 | </extension> | |
211 | 225 | </plugin> |
13 | 13 | <parent> |
14 | 14 | <artifactId>eclipse.platform.text</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | </parent> |
18 | 18 | <groupId>org.eclipse.ui</groupId> |
19 | 19 | <artifactId>org.eclipse.ui.genericeditor</artifactId> |
20 | <version>1.1.300-SNAPSHOT</version> | |
20 | <version>1.1.400-SNAPSHOT</version> | |
21 | 21 | <packaging>eclipse-plugin</packaging> |
22 | 22 | </project> |
82 | 82 | A core Expression that controls the enabled of the given auto edit strategies. The viewer, editor, and editor input are registered in the evaluation context as variable: |
83 | 83 | |
84 | 84 | * <with variable="viewer"/> : use it if your expression requires the viewer. |
85 | * <with variable="editor"/> : use it if your expression requires the editor. | |
86 | * <with variable="editorInput"/> : use it if your expression requires the editor input. | |
85 | * <with variable="document"/> : use it if your expression requires the document. | |
86 | * <with variable="editor"/> : use it if your expression requires the editor (deprecated, not always set). | |
87 | * <with variable="editorInput"/> : use it if your expression requires the editor input (deprecated, not always set). | |
87 | 88 | </documentation> |
88 | 89 | </annotation> |
89 | 90 | <complexType> |
82 | 82 | A core Expression that controls the enabled of the given character pair matcher. The viewer, editor, and editor input are registered in the evaluation context as variable: |
83 | 83 | |
84 | 84 | * <with variable="viewer"/> : use it if your expression requires the viewer. |
85 | * <with variable="editor"/> : use it if your expression requires the editor. | |
86 | * <with variable="editorInput"/> : use it if your expression requires the editor input. | |
85 | * <with variable="document"/> : use it if your expression requires the document. | |
86 | * <with variable="editor"/> : use it if your expression requires the editor (deprecated, not always set). | |
87 | * <with variable="editorInput"/> : use it if your expression requires the editor input (deprecated, not always set). | |
87 | 88 | </documentation> |
88 | 89 | </annotation> |
89 | 90 | <complexType> |
82 | 82 | A core Expression that controls the enabled of the given content assist processor. The viewer, editor, and editor input are registered in the evaluation context as variable: |
83 | 83 | |
84 | 84 | * <with variable="viewer"/> : use it if your expression requires the viewer. |
85 | * <with variable="editor"/> : use it if your expression requires the editor. | |
86 | * <with variable="editorInput"/> : use it if your expression requires the editor input. | |
85 | * <with variable="document"/> : use it if your expression requires the document. | |
86 | * <with variable="editor"/> : use it if your expression requires the editor (deprecated, not always set). | |
87 | * <with variable="editorInput"/> : use it if your expression requires the editor input (deprecated, not always set). | |
87 | 88 | </documentation> |
88 | 89 | </annotation> |
89 | 90 | <complexType> |
84 | 84 | A core Expression that controls the enabled of the given folding reconciler. The viewer, editor, and editor input are registered in the evaluation context as variable: |
85 | 85 | |
86 | 86 | * <with variable="viewer"/> : use it if your expression requires the viewer. |
87 | * <with variable="editor"/> : use it if your expression requires the editor. | |
88 | * <with variable="editorInput"/> : use it if your expression requires the editor input. | |
87 | * <with variable="document"/> : use it if your expression requires the document. | |
88 | * <with variable="editor"/> : use it if your expression requires the editor (deprecated, not always set). | |
89 | * <with variable="editorInput"/> : use it if your expression requires the editor input (deprecated, not always set). | |
89 | 90 | </documentation> |
90 | 91 | </annotation> |
91 | 92 | <complexType> |
82 | 82 | A core Expression that controls the enabled of the given highlight reconciler. The viewer, editor, and editor input are registered in the evaluation context as variable: |
83 | 83 | |
84 | 84 | * <with variable="viewer"/> : use it if your expression requires the viewer. |
85 | * <with variable="editor"/> : use it if your expression requires the editor. | |
86 | * <with variable="editorInput"/> : use it if your expression requires the editor input. | |
85 | * <with variable="document"/> : use it if your expression requires the document. | |
86 | * <with variable="editor"/> : use it if your expression requires the editor (deprecated, not always set). | |
87 | * <with variable="editorInput"/> : use it if your expression requires the editor input (deprecated, not always set). | |
87 | 88 | </documentation> |
88 | 89 | </annotation> |
89 | 90 | <complexType> |
111 | 111 | A core Expression that controls the enabled of the given hover provider. The viewer, editor, and editor input are registered in the evaluation context as variable: |
112 | 112 | |
113 | 113 | * <with variable="viewer"/> : use it if your expression requires the viewer. |
114 | * <with variable="editor"/> : use it if your expression requires the editor. | |
115 | * <with variable="editorInput"/> : use it if your expression requires the editor input. | |
114 | * <with variable="document"/> : use it if your expression requires the document. | |
115 | * <with variable="editor"/> : use it if your expression requires the editor (deprecated, not always set). | |
116 | * <with variable="editorInput"/> : use it if your expression requires the editor input (deprecated, not always set). | |
116 | 117 | </documentation> |
117 | 118 | </annotation> |
118 | 119 | <complexType> |
82 | 82 | A core Expression that controls the enabled of the given presentation reconciler. The viewer, editor, and editor input are registered in the evaluation context as variable: |
83 | 83 | |
84 | 84 | * <with variable="viewer"/> : use it if your expression requires the viewer. |
85 | * <with variable="editor"/> : use it if your expression requires the editor. | |
86 | * <with variable="editorInput"/> : use it if your expression requires the editor input. | |
85 | * <with variable="document"/> : use it if your expression requires the document. | |
86 | * <with variable="editor"/> : use it if your expression requires the editor (deprecated, not always set). | |
87 | * <with variable="editorInput"/> : use it if your expression requires the editor input (deprecated, not always set). | |
87 | 88 | </documentation> |
88 | 89 | </annotation> |
89 | 90 | <complexType> |
82 | 82 | A core Expression that controls the enabled of the given reconciler. The viewer, editor, and editor input are registered in the evaluation context as variable: |
83 | 83 | |
84 | 84 | * <with variable="viewer"/> : use it if your expression requires the viewer. |
85 | * <with variable="editor"/> : use it if your expression requires the editor. | |
86 | * <with variable="editorInput"/> : use it if your expression requires the editor input. | |
85 | * <with variable="document"/> : use it if your expression requires the document. | |
86 | * <with variable="editor"/> : use it if your expression requires the editor (deprecated, not always set). | |
87 | * <with variable="editorInput"/> : use it if your expression requires the editor input (deprecated, not always set). | |
87 | 88 | </documentation> |
88 | 89 | </annotation> |
89 | 90 | <complexType> |
+100
-100
0 | /** | |
1 | * Copyright (c) 2018 Angelo ZERR. | |
2 | * All rights reserved. This program and the accompanying materials | |
3 | * are made available under the terms of the Eclipse Public License v2.0 | |
4 | * which accompanies this distribution, and is available at | |
5 | * http://www.eclipse.org/legal/epl-v20.html | |
6 | * | |
7 | * Contributors: | |
8 | * Angelo Zerr <angelo.zerr@gmail.com> - Bug 538111 - [generic editor] Extension point for ICharacterPairMatcher | |
9 | */ | |
10 | package org.eclipse.ui.internal.genericeditor; | |
11 | ||
12 | import java.util.HashMap; | |
13 | import java.util.HashSet; | |
14 | import java.util.List; | |
15 | import java.util.Map; | |
16 | import java.util.Set; | |
17 | import java.util.stream.Collectors; | |
18 | ||
19 | import org.eclipse.core.runtime.IConfigurationElement; | |
20 | import org.eclipse.core.runtime.IRegistryChangeEvent; | |
21 | import org.eclipse.core.runtime.IRegistryChangeListener; | |
22 | import org.eclipse.core.runtime.IStatus; | |
23 | import org.eclipse.core.runtime.Platform; | |
24 | import org.eclipse.core.runtime.Status; | |
25 | import org.eclipse.core.runtime.content.IContentType; | |
26 | import org.eclipse.jface.text.source.ICharacterPairMatcher; | |
27 | import org.eclipse.jface.text.source.ISourceViewer; | |
28 | import org.eclipse.ui.texteditor.ITextEditor; | |
29 | ||
30 | /** | |
31 | * A registry of character pair matchers provided by extension | |
32 | * <code>org.eclipse.ui.genericeditor.characterPairMatchers</code>. Those | |
33 | * extensions are specific to a given {@link IContentType}. | |
34 | * | |
35 | * @since 1.2 | |
36 | */ | |
37 | public class CharacterPairMatcherRegistry { | |
38 | ||
39 | private static final String EXTENSION_POINT_ID = GenericEditorPlugin.BUNDLE_ID + ".characterPairMatchers"; //$NON-NLS-1$ | |
40 | ||
41 | private Map<IConfigurationElement, GenericContentTypeRelatedExtension<ICharacterPairMatcher>> extensions = new HashMap<>(); | |
42 | private boolean outOfSync = true; | |
43 | ||
44 | /** | |
45 | * Creates the registry and binds it to the extension point. | |
46 | */ | |
47 | public CharacterPairMatcherRegistry() { | |
48 | Platform.getExtensionRegistry().addRegistryChangeListener(new IRegistryChangeListener() { | |
49 | @Override | |
50 | public void registryChanged(IRegistryChangeEvent event) { | |
51 | outOfSync = true; | |
52 | } | |
53 | }, EXTENSION_POINT_ID); | |
54 | } | |
55 | ||
56 | /** | |
57 | * Get the contributed {@link IPresentationReconciliers}s that are relevant to | |
58 | * hook on source viewer according to document content types. | |
59 | * | |
60 | * @param sourceViewer the source viewer we're hooking completion to. | |
61 | * @param editor the text editor | |
62 | * @param contentTypes the content types of the document we're editing. | |
63 | * @return the list of {@link ICharacterPairMatcher} contributed for at least | |
64 | * one of the content types. | |
65 | */ | |
66 | public List<ICharacterPairMatcher> getCharacterPairMatchers(ISourceViewer sourceViewer, ITextEditor editor, | |
67 | Set<IContentType> contentTypes) { | |
68 | if (this.outOfSync) { | |
69 | sync(); | |
70 | } | |
71 | return this.extensions.values().stream().filter(ext -> contentTypes.contains(ext.targetContentType)) | |
72 | .filter(ext -> ext.matches(sourceViewer, editor)) | |
73 | .sorted(new ContentTypeSpecializationComparator<ICharacterPairMatcher>()) | |
74 | .map(GenericContentTypeRelatedExtension<ICharacterPairMatcher>::createDelegate) | |
75 | .collect(Collectors.toList()); | |
76 | } | |
77 | ||
78 | private void sync() { | |
79 | Set<IConfigurationElement> toRemoveExtensions = new HashSet<>(this.extensions.keySet()); | |
80 | for (IConfigurationElement extension : Platform.getExtensionRegistry() | |
81 | .getConfigurationElementsFor(EXTENSION_POINT_ID)) { | |
82 | toRemoveExtensions.remove(extension); | |
83 | if (!this.extensions.containsKey(extension)) { | |
84 | try { | |
85 | this.extensions.put(extension, | |
86 | new GenericContentTypeRelatedExtension<ICharacterPairMatcher>(extension)); | |
87 | } catch (Exception ex) { | |
88 | GenericEditorPlugin.getDefault().getLog() | |
89 | .log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, ex.getMessage(), ex)); | |
90 | } | |
91 | } | |
92 | } | |
93 | for (IConfigurationElement toRemove : toRemoveExtensions) { | |
94 | this.extensions.remove(toRemove); | |
95 | } | |
96 | this.outOfSync = false; | |
97 | } | |
98 | ||
99 | } | |
0 | /** | |
1 | * Copyright (c) 2018 Angelo ZERR. | |
2 | * All rights reserved. This program and the accompanying materials | |
3 | * are made available under the terms of the Eclipse Public License v2.0 | |
4 | * which accompanies this distribution, and is available at | |
5 | * http://www.eclipse.org/legal/epl-v20.html | |
6 | * | |
7 | * Contributors: | |
8 | * Angelo Zerr <angelo.zerr@gmail.com> - Bug 538111 - [generic editor] Extension point for ICharacterPairMatcher | |
9 | */ | |
10 | package org.eclipse.ui.internal.genericeditor; | |
11 | ||
12 | import java.util.HashMap; | |
13 | import java.util.HashSet; | |
14 | import java.util.List; | |
15 | import java.util.Map; | |
16 | import java.util.Set; | |
17 | import java.util.stream.Collectors; | |
18 | ||
19 | import org.eclipse.core.runtime.IConfigurationElement; | |
20 | import org.eclipse.core.runtime.IRegistryChangeEvent; | |
21 | import org.eclipse.core.runtime.IRegistryChangeListener; | |
22 | import org.eclipse.core.runtime.IStatus; | |
23 | import org.eclipse.core.runtime.Platform; | |
24 | import org.eclipse.core.runtime.Status; | |
25 | import org.eclipse.core.runtime.content.IContentType; | |
26 | import org.eclipse.jface.text.source.ICharacterPairMatcher; | |
27 | import org.eclipse.jface.text.source.ISourceViewer; | |
28 | import org.eclipse.ui.texteditor.ITextEditor; | |
29 | ||
30 | /** | |
31 | * A registry of character pair matchers provided by extension | |
32 | * <code>org.eclipse.ui.genericeditor.characterPairMatchers</code>. Those | |
33 | * extensions are specific to a given {@link IContentType}. | |
34 | * | |
35 | * @since 1.2 | |
36 | */ | |
37 | public class CharacterPairMatcherRegistry { | |
38 | ||
39 | private static final String EXTENSION_POINT_ID = GenericEditorPlugin.BUNDLE_ID + ".characterPairMatchers"; //$NON-NLS-1$ | |
40 | ||
41 | private Map<IConfigurationElement, GenericContentTypeRelatedExtension<ICharacterPairMatcher>> extensions = new HashMap<>(); | |
42 | private boolean outOfSync = true; | |
43 | ||
44 | /** | |
45 | * Creates the registry and binds it to the extension point. | |
46 | */ | |
47 | public CharacterPairMatcherRegistry() { | |
48 | Platform.getExtensionRegistry().addRegistryChangeListener(new IRegistryChangeListener() { | |
49 | @Override | |
50 | public void registryChanged(IRegistryChangeEvent event) { | |
51 | outOfSync = true; | |
52 | } | |
53 | }, EXTENSION_POINT_ID); | |
54 | } | |
55 | ||
56 | /** | |
57 | * Get the contributed {@link IPresentationReconciliers}s that are relevant to | |
58 | * hook on source viewer according to document content types. | |
59 | * | |
60 | * @param sourceViewer the source viewer we're hooking completion to. | |
61 | * @param editor the text editor | |
62 | * @param contentTypes the content types of the document we're editing. | |
63 | * @return the list of {@link ICharacterPairMatcher} contributed for at least | |
64 | * one of the content types. | |
65 | */ | |
66 | public List<ICharacterPairMatcher> getCharacterPairMatchers(ISourceViewer sourceViewer, ITextEditor editor, | |
67 | Set<IContentType> contentTypes) { | |
68 | if (this.outOfSync) { | |
69 | sync(); | |
70 | } | |
71 | return this.extensions.values().stream().filter(ext -> contentTypes.contains(ext.targetContentType)) | |
72 | .filter(ext -> ext.matches(sourceViewer, editor)) | |
73 | .sorted(new ContentTypeSpecializationComparator<ICharacterPairMatcher>()) | |
74 | .map(GenericContentTypeRelatedExtension<ICharacterPairMatcher>::createDelegate) | |
75 | .collect(Collectors.toList()); | |
76 | } | |
77 | ||
78 | private void sync() { | |
79 | Set<IConfigurationElement> toRemoveExtensions = new HashSet<>(this.extensions.keySet()); | |
80 | for (IConfigurationElement extension : Platform.getExtensionRegistry() | |
81 | .getConfigurationElementsFor(EXTENSION_POINT_ID)) { | |
82 | toRemoveExtensions.remove(extension); | |
83 | if (!this.extensions.containsKey(extension)) { | |
84 | try { | |
85 | this.extensions.put(extension, | |
86 | new GenericContentTypeRelatedExtension<ICharacterPairMatcher>(extension)); | |
87 | } catch (Exception ex) { | |
88 | GenericEditorPlugin.getDefault().getLog() | |
89 | .log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, ex.getMessage(), ex)); | |
90 | } | |
91 | } | |
92 | } | |
93 | for (IConfigurationElement toRemove : toRemoveExtensions) { | |
94 | this.extensions.remove(toRemove); | |
95 | } | |
96 | this.outOfSync = false; | |
97 | } | |
98 | ||
99 | } |
+3
-3
21 | 21 | private DefaultWordHighlightStrategy fStrategy; |
22 | 22 | |
23 | 23 | public DefaultWordHighlightReconciler() { |
24 | fStrategy = new DefaultWordHighlightStrategy(); | |
25 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
26 | } | |
24 | fStrategy = new DefaultWordHighlightStrategy(); | |
25 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
26 | } | |
27 | 27 | |
28 | 28 | @Override |
29 | 29 | public void install(ITextViewer textViewer) { |
+18
-31
45 | 45 | |
46 | 46 | /** |
47 | 47 | * |
48 | * This Reconciler Strategy is a default stategy which will be present if no | |
49 | * other highlightReconcilers are registered for a given content-type. It splits | |
50 | * the text into 'words' (which are defined as anything in-between | |
51 | * non-alphanumeric characters) and searches the document highlighting all like | |
52 | * words. | |
53 | * | |
54 | * E.g. if your file contains "t^he dog in the bog" and you leave your caret at | |
55 | * ^ you will get both instances of 'the' highlighted. | |
48 | * This Reconciler Strategy is a default stategy which will be present if no other highlightReconcilers are registered for a given content-type. It splits the text into 'words' (which are defined as | |
49 | * anything in-between non-alphanumeric characters) and searches the document highlighting all like words. | |
50 | * | |
51 | * E.g. if your file contains "t^he dog in the bog" and you leave your caret at ^ you will get both instances of 'the' highlighted. | |
56 | 52 | * |
57 | 53 | */ |
58 | public class DefaultWordHighlightStrategy | |
59 | implements IReconcilingStrategy, IReconcilingStrategyExtension, IPreferenceChangeListener { | |
54 | public class DefaultWordHighlightStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension, IPreferenceChangeListener { | |
60 | 55 | |
61 | 56 | private static final String ANNOTATION_TYPE = "org.eclipse.ui.genericeditor.text"; //$NON-NLS-1$ |
62 | 57 | |
84 | 79 | } |
85 | 80 | |
86 | 81 | String text = document.get(); |
87 | int offset = ((ITextViewerExtension5) sourceViewer).widgetOffset2ModelOffset(textSelection.getOffset()); | |
82 | int offset = textSelection.getOffset(); | |
83 | if (sourceViewer instanceof ITextViewerExtension5) { | |
84 | offset = ((ITextViewerExtension5) sourceViewer).widgetOffset2ModelOffset(textSelection.getOffset()); | |
85 | } | |
88 | 86 | |
89 | 87 | String word = findCurrentWord(text, offset); |
90 | 88 | if (word == null) { |
96 | 94 | Map<Annotation, Position> annotationMap = new HashMap<>(); |
97 | 95 | while (m.find()) { |
98 | 96 | if (m.group().equals(word)) { |
99 | annotationMap.put( | |
100 | new Annotation(ANNOTATION_TYPE, false, | |
101 | NLS.bind(Messages.DefaultWordHighlightStrategy_OccurrencesOf, word)), | |
102 | new Position(m.start(), m.end() - m.start())); | |
97 | annotationMap.put(new Annotation(ANNOTATION_TYPE, false, NLS.bind(Messages.DefaultWordHighlightStrategy_OccurrencesOf, word)), new Position(m.start(), m.end() - m.start())); | |
103 | 98 | } |
104 | 99 | } |
105 | 100 | |
153 | 148 | preferences.addPreferenceChangeListener(this); |
154 | 149 | this.enabled = preferences.getBoolean(ToggleHighlight.TOGGLE_HIGHLIGHT_PREFERENCE, true); |
155 | 150 | this.sourceViewer = (ISourceViewer) viewer; |
156 | ((IPostSelectionProvider) sourceViewer.getSelectionProvider()) | |
157 | .addPostSelectionChangedListener(editorSelectionChangedListener); | |
151 | ((IPostSelectionProvider) sourceViewer.getSelectionProvider()).addPostSelectionChangedListener(editorSelectionChangedListener); | |
158 | 152 | } |
159 | 153 | |
160 | 154 | public void uninstall() { |
161 | 155 | if (sourceViewer != null) { |
162 | ((IPostSelectionProvider) sourceViewer.getSelectionProvider()) | |
163 | .removePostSelectionChangedListener(editorSelectionChangedListener); | |
156 | ((IPostSelectionProvider) sourceViewer.getSelectionProvider()).removePostSelectionChangedListener(editorSelectionChangedListener); | |
164 | 157 | } |
165 | 158 | IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(GenericEditorPlugin.BUNDLE_ID); |
166 | 159 | preferences.removePreferenceChangeListener(this); |
167 | 160 | } |
168 | 161 | |
169 | @Override | |
170 | public void preferenceChange(PreferenceChangeEvent event) { | |
162 | @Override public void preferenceChange(PreferenceChangeEvent event) { | |
171 | 163 | if (event.getKey().equals(ToggleHighlight.TOGGLE_HIGHLIGHT_PREFERENCE)) { |
172 | 164 | this.enabled = Boolean.parseBoolean(event.getNewValue().toString()); |
173 | 165 | if (enabled) { |
178 | 170 | } |
179 | 171 | } |
180 | 172 | |
181 | @Override | |
182 | public void initialReconcile() { | |
173 | @Override public void initialReconcile() { | |
183 | 174 | if (sourceViewer != null) { |
184 | 175 | sourceViewer.getTextWidget().getDisplay().asyncExec(() -> { |
185 | 176 | if (sourceViewer != null && sourceViewer.getTextWidget() != null) { |
217 | 208 | return annotationModel; |
218 | 209 | } |
219 | 210 | |
220 | @Override | |
221 | public void setDocument(IDocument document) { | |
211 | @Override public void setDocument(IDocument document) { | |
222 | 212 | this.document = document; |
223 | 213 | } |
224 | 214 | |
225 | @Override | |
226 | public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
215 | @Override public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
227 | 216 | // Do nothing |
228 | 217 | } |
229 | 218 | |
230 | @Override | |
231 | public void reconcile(IRegion partition) { | |
219 | @Override public void reconcile(IRegion partition) { | |
232 | 220 | // Do nothing |
233 | 221 | } |
234 | 222 | |
235 | @Override | |
236 | public void setProgressMonitor(IProgressMonitor monitor) { | |
223 | @Override public void setProgressMonitor(IProgressMonitor monitor) { | |
237 | 224 | // Not used |
238 | 225 | } |
239 | 226 | } |
+15
-24
33 | 33 | import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; |
34 | 34 | |
35 | 35 | /** |
36 | * A generic code editor that is aimed at being extended by contributions. | |
37 | * Behavior is supposed to be added via extensions, not by inheritance. | |
36 | * A generic code editor that is aimed at being extended by contributions. Behavior is supposed to be added via extensions, not by inheritance. | |
38 | 37 | * |
39 | 38 | * @since 1.0 |
40 | 39 | */ |
60 | 59 | /** |
61 | 60 | * Initializes the key binding scopes of this generic code editor. |
62 | 61 | */ |
63 | @Override | |
64 | protected void initializeKeyBindingScopes() { | |
62 | @Override protected void initializeKeyBindingScopes() { | |
65 | 63 | setKeyBindingScopes(new String[] { CONTEXT_ID }); |
66 | 64 | } |
67 | 65 | |
68 | @Override | |
69 | protected void doSetInput(IEditorInput input) throws CoreException { | |
66 | @Override protected void doSetInput(IEditorInput input) throws CoreException { | |
70 | 67 | super.doSetInput(input); |
71 | 68 | configuration.watchDocument(getDocumentProvider().getDocument(input)); |
72 | 69 | } |
73 | 70 | |
74 | @Override | |
75 | protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) { | |
71 | @Override protected ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) { | |
76 | 72 | fAnnotationAccess = getAnnotationAccess(); |
77 | 73 | fOverviewRuler = createOverviewRuler(getSharedColors()); |
78 | 74 | |
79 | ProjectionViewer viewer = new ProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), | |
80 | styles); | |
75 | ProjectionViewer viewer = new ProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles); | |
81 | 76 | SourceViewerDecorationSupport support = getSourceViewerDecorationSupport(viewer); |
82 | 77 | configureCharacterPairMatcher(viewer, support); |
83 | 78 | return viewer; |
84 | 79 | } |
85 | 80 | |
86 | @Override | |
87 | public void createPartControl(Composite parent) { | |
81 | @Override public void createPartControl(Composite parent) { | |
88 | 82 | super.createPartControl(parent); |
89 | 83 | ProjectionViewer viewer = (ProjectionViewer) getSourceViewer(); |
90 | 84 | |
92 | 86 | viewer.doOperation(ProjectionViewer.TOGGLE); |
93 | 87 | } |
94 | 88 | |
95 | @Override | |
96 | protected void initializeEditor() { | |
89 | @Override protected void initializeEditor() { | |
97 | 90 | super.initializeEditor(); |
98 | setPreferenceStore(new ChainedPreferenceStore(new IPreferenceStore[] { | |
99 | GenericEditorPreferenceConstants.getPreferenceStore(), EditorsUI.getPreferenceStore() })); | |
91 | setPreferenceStore(new ChainedPreferenceStore(new IPreferenceStore[] { GenericEditorPreferenceConstants.getPreferenceStore(), EditorsUI.getPreferenceStore() })); | |
100 | 92 | } |
101 | 93 | |
102 | 94 | /** |
103 | * Configure the {@link ICharacterPairMatcher} from the | |
104 | * "org.eclipse.ui.genericeditor.characterPairMatchers" extension point. | |
95 | * Configure the {@link ICharacterPairMatcher} from the "org.eclipse.ui.genericeditor.characterPairMatchers" extension point. | |
105 | 96 | * |
106 | * @param viewer the source viewer. | |
97 | * @param viewer | |
98 | * the source viewer. | |
107 | 99 | * |
108 | * @param support the source viewer decoration support. | |
100 | * @param support | |
101 | * the source viewer decoration support. | |
109 | 102 | */ |
110 | 103 | private void configureCharacterPairMatcher(ISourceViewer viewer, SourceViewerDecorationSupport support) { |
111 | List<ICharacterPairMatcher> matchers = GenericEditorPlugin.getDefault().getCharacterPairMatcherRegistry() | |
112 | .getCharacterPairMatchers(viewer, this, configuration.getContentTypes()); | |
104 | List<ICharacterPairMatcher> matchers = GenericEditorPlugin.getDefault().getCharacterPairMatcherRegistry().getCharacterPairMatchers(viewer, this, configuration.getContentTypes(viewer)); | |
113 | 105 | if (!matchers.isEmpty()) { |
114 | 106 | ICharacterPairMatcher matcher = matchers.get(0); |
115 | 107 | support.setCharacterPairMatcher(matcher); |
116 | support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR, | |
117 | HIGHLIGHT_BRACKET_AT_CARET_LOCATION, ENCLOSING_BRACKETS); | |
108 | support.setMatchingCharacterPainterPreferenceKeys(MATCHING_BRACKETS, MATCHING_BRACKETS_COLOR, HIGHLIGHT_BRACKET_AT_CARET_LOCATION, ENCLOSING_BRACKETS); | |
118 | 109 | } |
119 | 110 | } |
120 | 111 | } |
+56
-58
17 | 17 | package org.eclipse.ui.internal.genericeditor; |
18 | 18 | |
19 | 19 | import java.util.Arrays; |
20 | import java.util.Collections; | |
20 | 21 | import java.util.LinkedHashSet; |
21 | 22 | import java.util.LinkedList; |
22 | 23 | import java.util.List; |
24 | 25 | import java.util.Queue; |
25 | 26 | import java.util.Set; |
26 | 27 | |
28 | import org.eclipse.core.filebuffers.FileBuffers; | |
29 | import org.eclipse.core.filebuffers.ITextFileBuffer; | |
27 | 30 | import org.eclipse.core.runtime.IAdaptable; |
28 | 31 | import org.eclipse.core.runtime.Platform; |
29 | 32 | import org.eclipse.core.runtime.content.IContentType; |
44 | 47 | import org.eclipse.jface.text.reconciler.IReconciler; |
45 | 48 | import org.eclipse.jface.text.source.ISourceViewer; |
46 | 49 | import org.eclipse.swt.widgets.Shell; |
47 | import org.eclipse.ui.IEditorPart; | |
48 | 50 | import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; |
49 | 51 | import org.eclipse.ui.internal.editors.text.EditorsPlugin; |
50 | 52 | import org.eclipse.ui.internal.genericeditor.folding.DefaultFoldingReconciler; |
54 | 56 | import org.eclipse.ui.texteditor.spelling.SpellingCorrectionProcessor; |
55 | 57 | |
56 | 58 | /** |
57 | * The configuration of the {@link ExtensionBasedTextEditor}. It registers the | |
58 | * proxy composite for hover, completion, syntax highlighting, and then those | |
59 | * proxy take care of resolving to the right extensions on-demand. | |
59 | * The configuration of the {@link ExtensionBasedTextEditor}. It registers the proxy composite for hover, completion, syntax highlighting, and then those proxy take care of resolving to the right | |
60 | * extensions on-demand. | |
60 | 61 | * |
61 | 62 | * @since 1.0 |
62 | 63 | */ |
63 | 64 | @SuppressWarnings("restriction") |
64 | public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewerConfiguration | |
65 | implements IDocumentPartitioningListener { | |
65 | public final class ExtensionBasedTextViewerConfiguration extends TextSourceViewerConfiguration implements IDocumentPartitioningListener { | |
66 | 66 | |
67 | 67 | private ITextEditor editor; |
68 | 68 | private Set<IContentType> contentTypes; |
73 | 73 | |
74 | 74 | /** |
75 | 75 | * |
76 | * @param editor the editor we're creating. | |
77 | * @param preferenceStore the preference store. | |
76 | * @param editor | |
77 | * the editor we're creating. | |
78 | * @param preferenceStore | |
79 | * the preference store. | |
78 | 80 | */ |
79 | 81 | public ExtensionBasedTextViewerConfiguration(ITextEditor editor, IPreferenceStore preferenceStore) { |
80 | 82 | super(preferenceStore); |
81 | 83 | this.editor = editor; |
82 | this.editor.addPropertyListener((source, propId) -> { | |
83 | if (propId == IEditorPart.PROP_INPUT) { | |
84 | watchDocument(editor.getDocumentProvider().getDocument(editor.getEditorInput())); | |
85 | } | |
86 | }); | |
87 | } | |
88 | ||
89 | Set<IContentType> getContentTypes() { | |
84 | } | |
85 | ||
86 | Set<IContentType> getContentTypes(ISourceViewer viewer) { | |
90 | 87 | if (this.contentTypes == null) { |
91 | 88 | this.contentTypes = new LinkedHashSet<>(); |
92 | Queue<IContentType> types = new LinkedList<>(Arrays | |
93 | .asList(Platform.getContentTypeManager().findContentTypesFor(editor.getEditorInput().getName()))); | |
89 | String fileName = null; | |
90 | if (this.editor != null) { | |
91 | fileName = editor.getEditorInput().getName(); | |
92 | } else { | |
93 | IDocument document = viewer.getDocument(); | |
94 | if (document != null) { | |
95 | ITextFileBuffer buffer = FileBuffers.getTextFileBufferManager().getTextFileBuffer(document); | |
96 | if (buffer != null) { | |
97 | fileName = buffer.getLocation().lastSegment(); | |
98 | } | |
99 | } | |
100 | } | |
101 | if (fileName == null) { | |
102 | return Collections.emptySet(); | |
103 | } | |
104 | Queue<IContentType> types = new LinkedList<>(Arrays.asList(Platform.getContentTypeManager().findContentTypesFor(fileName))); | |
94 | 105 | while (!types.isEmpty()) { |
95 | 106 | IContentType type = types.poll(); |
96 | 107 | this.contentTypes.add(type); |
103 | 114 | return this.contentTypes; |
104 | 115 | } |
105 | 116 | |
106 | @Override | |
107 | public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { | |
108 | List<ITextHover> hovers = GenericEditorPlugin.getDefault().getHoverRegistry().getAvailableHovers(sourceViewer, | |
109 | editor, getContentTypes()); | |
117 | @Override public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { | |
118 | List<ITextHover> hovers = GenericEditorPlugin.getDefault().getHoverRegistry().getAvailableHovers(sourceViewer, editor, getContentTypes(sourceViewer)); | |
110 | 119 | if (hovers == null || hovers.isEmpty()) { |
111 | 120 | return null; |
112 | 121 | } else if (hovers.size() == 1) { |
116 | 125 | } |
117 | 126 | } |
118 | 127 | |
119 | @Override | |
120 | public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { | |
128 | @Override public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { | |
121 | 129 | ContentAssistProcessorRegistry registry = GenericEditorPlugin.getDefault().getContentAssistProcessorRegistry(); |
122 | 130 | contentAssistant = new ContentAssistant(true); |
123 | 131 | contentAssistant.setContextInformationPopupOrientation(ContentAssistant.CONTEXT_INFO_BELOW); |
125 | 133 | contentAssistant.setAutoActivationDelay(0); |
126 | 134 | contentAssistant.enableColoredLabels(true); |
127 | 135 | contentAssistant.enableAutoActivation(true); |
128 | this.processors = registry.getContentAssistProcessors(sourceViewer, editor, getContentTypes()); | |
136 | this.processors = registry.getContentAssistProcessors(sourceViewer, editor, getContentTypes(sourceViewer)); | |
129 | 137 | if (this.processors.isEmpty()) { |
130 | 138 | this.processors.add(new DefaultContentAssistProcessor()); |
131 | 139 | } |
136 | 144 | associateTokenContentTypes(this.document); |
137 | 145 | } |
138 | 146 | contentAssistant.setInformationControlCreator(new AbstractReusableInformationControlCreator() { |
139 | @Override | |
140 | protected IInformationControl doCreateInformationControl(Shell parent) { | |
147 | @Override protected IInformationControl doCreateInformationControl(Shell parent) { | |
141 | 148 | return new DefaultInformationControl(parent); |
142 | 149 | } |
143 | 150 | }); |
151 | watchDocument(sourceViewer.getDocument()); | |
144 | 152 | return contentAssistant; |
145 | 153 | } |
146 | 154 | |
147 | @Override | |
148 | public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { | |
155 | @Override public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { | |
149 | 156 | PresentationReconcilerRegistry registry = GenericEditorPlugin.getDefault().getPresentationReconcilerRegistry(); |
150 | List<IPresentationReconciler> reconciliers = registry.getPresentationReconcilers(sourceViewer, editor, | |
151 | getContentTypes()); | |
157 | List<IPresentationReconciler> reconciliers = registry.getPresentationReconcilers(sourceViewer, editor, getContentTypes(sourceViewer)); | |
152 | 158 | if (!reconciliers.isEmpty()) { |
153 | 159 | return reconciliers.get(0); |
154 | 160 | } |
162 | 168 | if (this.document != null) { |
163 | 169 | this.document.removeDocumentPartitioningListener(this); |
164 | 170 | } |
165 | this.document = document; | |
166 | associateTokenContentTypes(document); | |
167 | document.addDocumentPartitioningListener(this); | |
168 | } | |
169 | ||
170 | @Override | |
171 | public void documentPartitioningChanged(IDocument document) { | |
171 | if (document != null) { | |
172 | this.document = document; | |
173 | associateTokenContentTypes(document); | |
174 | document.addDocumentPartitioningListener(this); | |
175 | } | |
176 | } | |
177 | ||
178 | @Override public void documentPartitioningChanged(IDocument document) { | |
172 | 179 | associateTokenContentTypes(document); |
173 | 180 | } |
174 | 181 | |
183 | 190 | } |
184 | 191 | } |
185 | 192 | |
186 | @Override | |
187 | public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) { | |
193 | @Override public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) { | |
188 | 194 | QuickAssistAssistant quickAssistAssistant = new QuickAssistAssistant(); |
189 | CompositeQuickAssistProcessor processor = new CompositeQuickAssistProcessor( | |
190 | Arrays.asList(new MarkerResoltionQuickAssistProcessor(), new SpellingCorrectionProcessor())); | |
195 | CompositeQuickAssistProcessor processor = new CompositeQuickAssistProcessor(Arrays.asList(new MarkerResoltionQuickAssistProcessor(), new SpellingCorrectionProcessor())); | |
191 | 196 | quickAssistAssistant.setQuickAssistProcessor(processor); |
192 | quickAssistAssistant.setRestoreCompletionProposalSize( | |
193 | EditorsPlugin.getDefault().getDialogSettingsSection("quick_assist_proposal_size")); //$NON-NLS-1$ | |
194 | quickAssistAssistant.setInformationControlCreator( | |
195 | parent -> new DefaultInformationControl(parent, EditorsPlugin.getAdditionalInfoAffordanceString())); | |
197 | quickAssistAssistant.setRestoreCompletionProposalSize(EditorsPlugin.getDefault().getDialogSettingsSection("quick_assist_proposal_size")); //$NON-NLS-1$ | |
198 | quickAssistAssistant.setInformationControlCreator(parent -> new DefaultInformationControl(parent, EditorsPlugin.getAdditionalInfoAffordanceString())); | |
196 | 199 | return quickAssistAssistant; |
197 | 200 | } |
198 | 201 | |
199 | @Override | |
200 | public IReconciler getReconciler(ISourceViewer sourceViewer) { | |
202 | @Override public IReconciler getReconciler(ISourceViewer sourceViewer) { | |
201 | 203 | ReconcilerRegistry registry = GenericEditorPlugin.getDefault().getReconcilerRegistry(); |
202 | List<IReconciler> reconcilers = registry.getReconcilers(sourceViewer, editor, getContentTypes()); | |
204 | List<IReconciler> reconcilers = registry.getReconcilers(sourceViewer, editor, getContentTypes(sourceViewer)); | |
203 | 205 | // Fill with highlight reconcilers |
204 | List<IReconciler> highlightReconcilers = registry.getHighlightReconcilers(sourceViewer, editor, | |
205 | getContentTypes()); | |
206 | List<IReconciler> highlightReconcilers = registry.getHighlightReconcilers(sourceViewer, editor, getContentTypes(sourceViewer)); | |
206 | 207 | if (!highlightReconcilers.isEmpty()) { |
207 | 208 | reconcilers.addAll(highlightReconcilers); |
208 | 209 | } else { |
209 | 210 | reconcilers.add(new DefaultWordHighlightReconciler()); |
210 | 211 | } |
211 | 212 | // Fill with folding reconcilers |
212 | List<IReconciler> foldingReconcilers = registry.getFoldingReconcilers(sourceViewer, editor, getContentTypes()); | |
213 | List<IReconciler> foldingReconcilers = registry.getFoldingReconcilers(sourceViewer, editor, getContentTypes(sourceViewer)); | |
213 | 214 | if (!foldingReconcilers.isEmpty()) { |
214 | 215 | reconcilers.addAll(foldingReconcilers); |
215 | 216 | } else { |
222 | 223 | return null; |
223 | 224 | } |
224 | 225 | |
225 | @Override | |
226 | public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) { | |
226 | @Override public IAutoEditStrategy[] getAutoEditStrategies(ISourceViewer sourceViewer, String contentType) { | |
227 | 227 | AutoEditStrategyRegistry registry = GenericEditorPlugin.getDefault().getAutoEditStrategyRegistry(); |
228 | List<IAutoEditStrategy> editStrategies = registry.getAutoEditStrategies(sourceViewer, editor, | |
229 | getContentTypes()); | |
228 | List<IAutoEditStrategy> editStrategies = registry.getAutoEditStrategies(sourceViewer, editor, getContentTypes(sourceViewer)); | |
230 | 229 | if (!editStrategies.isEmpty()) { |
231 | 230 | return editStrategies.toArray(new IAutoEditStrategy[editStrategies.size()]); |
232 | 231 | } |
233 | 232 | return super.getAutoEditStrategies(sourceViewer, contentType); |
234 | 233 | } |
235 | 234 | |
236 | @Override | |
237 | protected Map<String, IAdaptable> getHyperlinkDetectorTargets(ISourceViewer sourceViewer) { | |
235 | @Override protected Map<String, IAdaptable> getHyperlinkDetectorTargets(ISourceViewer sourceViewer) { | |
238 | 236 | Map<String, IAdaptable> targets = super.getHyperlinkDetectorTargets(sourceViewer); |
239 | 237 | targets.put("org.eclipse.ui.genericeditor.GenericEditor", editor); //$NON-NLS-1$ |
240 | 238 | return targets; |
+28
-28
27 | 27 | import org.eclipse.ui.texteditor.ITextEditor; |
28 | 28 | |
29 | 29 | /** |
30 | * This class wraps and proxies an instance of T provided through extensions | |
31 | * and loads it lazily when it can contribute to the editor, then delegates all operations to | |
32 | * actual instance. | |
30 | * This class wraps and proxies an instance of T provided through extensions and loads it lazily when it can contribute to the editor, then delegates all operations to actual instance. | |
33 | 31 | * |
34 | * @param <T> the actual type to proxy, typically the one defined on the extension point. | |
32 | * @param <T> | |
33 | * the actual type to proxy, typically the one defined on the extension point. | |
35 | 34 | */ |
36 | 35 | public class GenericContentTypeRelatedExtension<T> { |
37 | 36 | private static final String ID_ATTRIBUTE = "id"; //$NON-NLS-1$ |
45 | 44 | |
46 | 45 | public GenericContentTypeRelatedExtension(IConfigurationElement element) throws Exception { |
47 | 46 | this.extension = element; |
48 | this.targetContentType = Platform.getContentTypeManager() | |
49 | .getContentType(element.getAttribute(CONTENT_TYPE_ATTRIBUTE)); | |
47 | this.targetContentType = Platform.getContentTypeManager().getContentType(element.getAttribute(CONTENT_TYPE_ATTRIBUTE)); | |
50 | 48 | this.enabledWhen = buildEnabledWhen(element); |
51 | 49 | } |
52 | 50 | |
53 | @SuppressWarnings("unchecked") | |
54 | public T createDelegate() { | |
51 | @SuppressWarnings("unchecked") public T createDelegate() { | |
55 | 52 | try { |
56 | 53 | return (T) extension.createExecutableExtension(CLASS_ATTRIBUTE); |
57 | 54 | } catch (CoreException e) { |
61 | 58 | } |
62 | 59 | |
63 | 60 | /** |
64 | * Returns the expression {@link Expression} declared in the | |
65 | * <code>enabledWhen</code> element. | |
61 | * Returns the expression {@link Expression} declared in the <code>enabledWhen</code> element. | |
66 | 62 | * |
67 | * @param configElement the configuration element | |
68 | * @return the expression {@link Expression} declared in the enabledWhen | |
69 | * element. | |
70 | * @throws CoreException when enabledWhen expression is not valid. | |
63 | * @param configElement | |
64 | * the configuration element | |
65 | * @return the expression {@link Expression} declared in the enabledWhen element. | |
66 | * @throws CoreException | |
67 | * when enabledWhen expression is not valid. | |
71 | 68 | */ |
72 | 69 | private static Expression buildEnabledWhen(IConfigurationElement configElement) throws CoreException { |
73 | 70 | final IConfigurationElement[] children = configElement.getChildren(ENABLED_WHEN_ATTRIBUTE); |
74 | 71 | if (children.length > 0) { |
75 | 72 | IConfigurationElement[] subChildren = children[0].getChildren(); |
76 | 73 | if (subChildren.length != 1) { |
77 | throw new CoreException(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, | |
78 | "One <enabledWhen> element is accepted. Disabling " //$NON-NLS-1$ | |
79 | + configElement.getAttribute(ID_ATTRIBUTE))); | |
74 | throw new CoreException(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, "One <enabledWhen> element is accepted. Disabling " //$NON-NLS-1$ | |
75 | + configElement.getAttribute(ID_ATTRIBUTE))); | |
80 | 76 | } |
81 | 77 | final ElementHandler elementHandler = ElementHandler.getDefault(); |
82 | 78 | final ExpressionConverter converter = ExpressionConverter.getDefault(); |
86 | 82 | } |
87 | 83 | |
88 | 84 | /** |
89 | * Returns true if the given viewer, editor matches the enabledWhen expression | |
90 | * and false otherwise. | |
85 | * Returns true if the given viewer, editor matches the enabledWhen expression and false otherwise. | |
91 | 86 | * |
92 | * @param viewer the viewer | |
93 | * @param editor the editor | |
94 | * @return true if the given viewer, editor matches the enabledWhen expression | |
95 | * and false otherwise. | |
87 | * @param viewer | |
88 | * the viewer | |
89 | * @param editor | |
90 | * the editor | |
91 | * @return true if the given viewer, editor matches the enabledWhen expression and false otherwise. | |
96 | 92 | */ |
97 | 93 | public boolean matches(ISourceViewer viewer, ITextEditor editor) { |
98 | 94 | if (enabledWhen == null) { |
99 | 95 | return true; |
100 | 96 | } |
101 | EvaluationContext context = new EvaluationContext(null, editor); | |
97 | EvaluationContext context = new EvaluationContext(null, editor != null ? editor : viewer); | |
102 | 98 | context.setAllowPluginActivation(true); |
103 | 99 | context.addVariable("viewer", viewer); //$NON-NLS-1$ |
104 | context.addVariable("editor", editor); //$NON-NLS-1$ | |
105 | context.addVariable("editorInput", editor.getEditorInput()); //$NON-NLS-1$ | |
100 | if (viewer.getDocument() != null) { | |
101 | context.addVariable("document", viewer.getDocument()); //$NON-NLS-1$ | |
102 | } | |
103 | if (editor != null) { | |
104 | context.addVariable("editor", editor); //$NON-NLS-1$ | |
105 | context.addVariable("editorInput", editor.getEditorInput()); //$NON-NLS-1$ | |
106 | } | |
106 | 107 | try { |
107 | 108 | return enabledWhen.evaluate(context) == EvaluationResult.TRUE; |
108 | 109 | } catch (CoreException e) { |
109 | GenericEditorPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, | |
110 | "Error while 'enabledWhen' evaluation", e)); //$NON-NLS-1$ | |
110 | GenericEditorPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, GenericEditorPlugin.BUNDLE_ID, "Error while 'enabledWhen' evaluation", e)); //$NON-NLS-1$ | |
111 | 111 | return false; |
112 | 112 | } |
113 | 113 | } |
+27
-0
0 | /******************************************************************************* | |
1 | * Copyright (c) 2019 Red Hat Inc. and others. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * - Mickael Istria (Red Hat Inc.) | |
12 | *******************************************************************************/ | |
13 | package org.eclipse.ui.internal.genericeditor.compare; | |
14 | ||
15 | import org.eclipse.compare.CompareConfiguration; | |
16 | import org.eclipse.compare.IViewerCreator; | |
17 | import org.eclipse.jface.viewers.Viewer; | |
18 | import org.eclipse.swt.widgets.Composite; | |
19 | ||
20 | public class CompareViewerCreator implements IViewerCreator { | |
21 | ||
22 | @Override public Viewer createViewer(Composite parent, CompareConfiguration compareConfiguration) { | |
23 | return new GenericEditorMergeViewer(parent, compareConfiguration); | |
24 | } | |
25 | ||
26 | } |
+53
-0
0 | /******************************************************************************* | |
1 | * Copyright (c) 2019 Red Hat Inc. and others. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * - Mickael Istria (Red Hat Inc.) | |
12 | *******************************************************************************/ | |
13 | package org.eclipse.ui.internal.genericeditor.compare; | |
14 | ||
15 | import org.eclipse.compare.CompareConfiguration; | |
16 | import org.eclipse.compare.contentmergeviewer.TextMergeViewer; | |
17 | import org.eclipse.jface.text.IDocument; | |
18 | import org.eclipse.jface.text.ITextInputListener; | |
19 | import org.eclipse.jface.text.TextViewer; | |
20 | import org.eclipse.jface.text.source.ISourceViewer; | |
21 | import org.eclipse.jface.text.source.SourceViewer; | |
22 | import org.eclipse.swt.widgets.Composite; | |
23 | import org.eclipse.ui.internal.genericeditor.ExtensionBasedTextViewerConfiguration; | |
24 | import org.eclipse.ui.internal.genericeditor.GenericEditorPlugin; | |
25 | ||
26 | public class GenericEditorMergeViewer extends TextMergeViewer { | |
27 | ||
28 | public GenericEditorMergeViewer(Composite parent, CompareConfiguration configuration) { | |
29 | super(parent, configuration); | |
30 | } | |
31 | ||
32 | @Override protected SourceViewer createSourceViewer(Composite parent, int textOrientation) { | |
33 | SourceViewer res = super.createSourceViewer(parent, textOrientation); | |
34 | res.addTextInputListener(new ITextInputListener() { | |
35 | @Override public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { | |
36 | configureTextViewer(res); | |
37 | } | |
38 | ||
39 | @Override public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { | |
40 | // Nothing to do | |
41 | } | |
42 | }); | |
43 | return res; | |
44 | } | |
45 | ||
46 | @Override protected void configureTextViewer(TextViewer textViewer) { | |
47 | if (textViewer.getDocument() != null && textViewer instanceof ISourceViewer) { | |
48 | ((ISourceViewer) textViewer).configure(new ExtensionBasedTextViewerConfiguration(null, GenericEditorPlugin.getDefault().getPreferenceStore())); | |
49 | } | |
50 | } | |
51 | ||
52 | } |
+14
-17
28 | 28 | this.foldingStrategy = new IndentFoldingStrategy(); |
29 | 29 | } |
30 | 30 | |
31 | @Override | |
32 | public void install(ITextViewer textViewer) { | |
31 | @Override public void install(ITextViewer textViewer) { | |
33 | 32 | super.install(textViewer); |
34 | ProjectionViewer viewer = (ProjectionViewer) textViewer; | |
35 | foldingStrategy.setViewer(viewer); | |
33 | if (textViewer instanceof ProjectionViewer) { | |
34 | ProjectionViewer viewer = (ProjectionViewer) textViewer; | |
35 | foldingStrategy.setViewer(viewer); | |
36 | } | |
36 | 37 | } |
37 | 38 | |
38 | @Override | |
39 | public void uninstall() { | |
39 | @Override public void uninstall() { | |
40 | 40 | super.uninstall(); |
41 | foldingStrategy.uninstall(); | |
41 | if (foldingStrategy != null) { | |
42 | foldingStrategy.uninstall(); | |
43 | } | |
42 | 44 | } |
43 | 45 | |
44 | @Override | |
45 | protected void process(DirtyRegion dirtyRegion) { | |
46 | @Override protected void process(DirtyRegion dirtyRegion) { | |
46 | 47 | foldingStrategy.reconcile(dirtyRegion, null); |
47 | 48 | } |
48 | 49 | |
49 | @Override | |
50 | protected void reconcilerDocumentChanged(IDocument newDocument) { | |
50 | @Override protected void reconcilerDocumentChanged(IDocument newDocument) { | |
51 | 51 | foldingStrategy.setDocument(newDocument); |
52 | 52 | } |
53 | 53 | |
54 | @Override | |
55 | public IReconcilingStrategy getReconcilingStrategy(String contentType) { | |
54 | @Override public IReconcilingStrategy getReconcilingStrategy(String contentType) { | |
56 | 55 | return foldingStrategy; |
57 | 56 | } |
58 | 57 | |
59 | @Override | |
60 | public void setProgressMonitor(IProgressMonitor monitor) { | |
58 | @Override public void setProgressMonitor(IProgressMonitor monitor) { | |
61 | 59 | super.setProgressMonitor(monitor); |
62 | 60 | foldingStrategy.setProgressMonitor(monitor); |
63 | 61 | } |
64 | 62 | |
65 | @Override | |
66 | protected void initialProcess() { | |
63 | @Override protected void initialProcess() { | |
67 | 64 | super.initialProcess(); |
68 | 65 | foldingStrategy.initialReconcile(); |
69 | 66 | } |
+467
-467
0 | /******************************************************************************* | |
1 | * Copyright (c) 2009, 2018 IBM Corporation and others. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * IBM Corporation - initial API and implementation | |
12 | * Angelo Zerr <angelo.zerr@gmail.com> - adapt code org.eclipse.wst.sse.ui.internal.projection.AbstractStructuredFoldingStrategy to support generic indent folding strategy. | |
13 | * [generic editor] Default Code folding for generic editor should use IndentFoldingStrategy - Bug 520659 | |
14 | */ | |
15 | package org.eclipse.ui.internal.genericeditor.folding; | |
16 | ||
17 | import java.util.ArrayList; | |
18 | import java.util.HashMap; | |
19 | import java.util.Iterator; | |
20 | import java.util.List; | |
21 | import java.util.Map; | |
22 | ||
23 | import org.eclipse.core.runtime.IProgressMonitor; | |
24 | import org.eclipse.jface.text.BadLocationException; | |
25 | import org.eclipse.jface.text.IDocument; | |
26 | import org.eclipse.jface.text.IRegion; | |
27 | import org.eclipse.jface.text.Position; | |
28 | import org.eclipse.jface.text.reconciler.DirtyRegion; | |
29 | import org.eclipse.jface.text.reconciler.IReconcilingStrategy; | |
30 | import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; | |
31 | import org.eclipse.jface.text.source.Annotation; | |
32 | import org.eclipse.jface.text.source.projection.IProjectionListener; | |
33 | import org.eclipse.jface.text.source.projection.ProjectionAnnotation; | |
34 | import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; | |
35 | import org.eclipse.jface.text.source.projection.ProjectionViewer; | |
36 | import org.eclipse.swt.graphics.FontMetrics; | |
37 | import org.eclipse.swt.graphics.GC; | |
38 | import org.eclipse.swt.graphics.Rectangle; | |
39 | import org.eclipse.swt.widgets.Canvas; | |
40 | ||
41 | /** | |
42 | * Indent folding strategy to fold code by using indentation. The folding | |
43 | * strategy must be associated with a viewer for it to function. | |
44 | */ | |
45 | public class IndentFoldingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension, IProjectionListener { | |
46 | ||
47 | private IDocument document; | |
48 | private ProjectionViewer viewer; | |
49 | private ProjectionAnnotationModel projectionAnnotationModel; | |
50 | private final String lineStartsWithKeyword; | |
51 | ||
52 | public IndentFoldingStrategy() { | |
53 | this(null); | |
54 | } | |
55 | ||
56 | public IndentFoldingStrategy(String lineStartsWithKeyword) { | |
57 | this.lineStartsWithKeyword = lineStartsWithKeyword; | |
58 | } | |
59 | ||
60 | /** | |
61 | * A FoldingAnnotation is a {@link ProjectionAnnotation} it is folding and | |
62 | * overriding the paint method (in a hacky type way) to prevent one line folding | |
63 | * annotations to be drawn. | |
64 | */ | |
65 | protected class FoldingAnnotation extends ProjectionAnnotation { | |
66 | private boolean visible; /* workaround for BUG85874 */ | |
67 | ||
68 | /** | |
69 | * Creates a new FoldingAnnotation. | |
70 | * | |
71 | * @param isCollapsed true if this annotation should be collapsed, false | |
72 | * otherwise | |
73 | */ | |
74 | public FoldingAnnotation(boolean isCollapsed) { | |
75 | super(isCollapsed); | |
76 | visible = false; | |
77 | } | |
78 | ||
79 | /** | |
80 | * Does not paint hidden annotations. Annotations are hidden when they only span | |
81 | * one line. | |
82 | * | |
83 | * @see ProjectionAnnotation#paint(org.eclipse.swt.graphics.GC, | |
84 | * org.eclipse.swt.widgets.Canvas, org.eclipse.swt.graphics.Rectangle) | |
85 | */ | |
86 | @Override | |
87 | public void paint(GC gc, Canvas canvas, Rectangle rectangle) { | |
88 | /* workaround for BUG85874 */ | |
89 | /* | |
90 | * only need to check annotations that are expanded because hidden annotations | |
91 | * should never have been given the chance to collapse. | |
92 | */ | |
93 | if (!isCollapsed()) { | |
94 | // working with rectangle, so line height | |
95 | FontMetrics metrics = gc.getFontMetrics(); | |
96 | if (metrics != null) { | |
97 | // do not draw annotations that only span one line and | |
98 | // mark them as not visible | |
99 | if ((rectangle.height / metrics.getHeight()) <= 1) { | |
100 | visible = false; | |
101 | return; | |
102 | } | |
103 | } | |
104 | } | |
105 | visible = true; | |
106 | super.paint(gc, canvas, rectangle); | |
107 | } | |
108 | ||
109 | @Override | |
110 | public void markCollapsed() { | |
111 | /* workaround for BUG85874 */ | |
112 | // do not mark collapsed if annotation is not visible | |
113 | if (visible) | |
114 | super.markCollapsed(); | |
115 | } | |
116 | } | |
117 | ||
118 | /** | |
119 | * The folding strategy must be associated with a viewer for it to function | |
120 | * | |
121 | * @param viewer the viewer to associate this folding strategy with | |
122 | */ | |
123 | public void setViewer(ProjectionViewer viewer) { | |
124 | if (this.viewer != null) { | |
125 | this.viewer.removeProjectionListener(this); | |
126 | } | |
127 | this.viewer = viewer; | |
128 | this.viewer.addProjectionListener(this); | |
129 | this.projectionAnnotationModel = this.viewer.getProjectionAnnotationModel(); | |
130 | } | |
131 | ||
132 | public void uninstall() { | |
133 | setDocument(null); | |
134 | ||
135 | if (viewer != null) { | |
136 | viewer.removeProjectionListener(this); | |
137 | viewer = null; | |
138 | } | |
139 | ||
140 | projectionDisabled(); | |
141 | } | |
142 | ||
143 | @Override | |
144 | public void setDocument(IDocument document) { | |
145 | this.document = document; | |
146 | } | |
147 | ||
148 | @Override | |
149 | public void projectionDisabled() { | |
150 | projectionAnnotationModel = null; | |
151 | } | |
152 | ||
153 | @Override | |
154 | public void projectionEnabled() { | |
155 | if (viewer != null) { | |
156 | projectionAnnotationModel = viewer.getProjectionAnnotationModel(); | |
157 | } | |
158 | } | |
159 | ||
160 | private class LineIndent { | |
161 | public int line; | |
162 | public final int indent; | |
163 | ||
164 | public LineIndent(int line, int indent) { | |
165 | this.line = line; | |
166 | this.indent = indent; | |
167 | } | |
168 | } | |
169 | ||
170 | @Override | |
171 | public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
172 | if (projectionAnnotationModel != null) { | |
173 | ||
174 | // these are what are passed off to the annotation model to | |
175 | // actually create and maintain the annotations | |
176 | List<Annotation> modifications = new ArrayList<Annotation>(); | |
177 | List<FoldingAnnotation> deletions = new ArrayList<FoldingAnnotation>(); | |
178 | List<FoldingAnnotation> existing = new ArrayList<FoldingAnnotation>(); | |
179 | Map<Annotation, Position> additions = new HashMap<Annotation, Position>(); | |
180 | ||
181 | // find and mark all folding annotations with length 0 for deletion | |
182 | markInvalidAnnotationsForDeletion(dirtyRegion, deletions, existing); | |
183 | ||
184 | List<LineIndent> previousRegions = new ArrayList<LineIndent>(); | |
185 | ||
186 | int tabSize = 1; | |
187 | int minimumRangeSize = 1; | |
188 | try { | |
189 | ||
190 | // Today we recompute annotation from the whole document each | |
191 | // time. | |
192 | // performance s good even with large document, but it should be | |
193 | // better to loop for only DirtyRegion (and before/after) | |
194 | // int offset = dirtyRegion.getOffset(); | |
195 | // int length = dirtyRegion.getLength(); | |
196 | // int startLine = 0; //document.getLineOfOffset(offset); | |
197 | int endLine = document.getNumberOfLines() - 1; // startLine + | |
198 | // document.getNumberOfLines(offset, | |
199 | // length) - 1; | |
200 | ||
201 | // sentinel, to make sure there's at least one entry | |
202 | previousRegions.add(new LineIndent(endLine + 1, -1)); | |
203 | ||
204 | int lastLineWhichIsNotEmpty = 0; | |
205 | int lineEmptyCount = 0; | |
206 | Integer lastLineForKeyword = null; | |
207 | int line = endLine; | |
208 | for (line = endLine; line >= 0; line--) { | |
209 | int lineOffset = document.getLineOffset(line); | |
210 | String delim = document.getLineDelimiter(line); | |
211 | int lineLength = document.getLineLength(line) - (delim != null ? delim.length() : 0); | |
212 | String lineContent = document.get(lineOffset, lineLength); | |
213 | ||
214 | LineState state = getLineState(lineContent, lastLineForKeyword); | |
215 | switch (state) { | |
216 | case StartWithKeyWord: | |
217 | lineEmptyCount = 0; | |
218 | lastLineWhichIsNotEmpty = line; | |
219 | if (lastLineForKeyword == null) { | |
220 | lastLineForKeyword = line; | |
221 | } | |
222 | break; | |
223 | case EmptyLine: | |
224 | lineEmptyCount++; | |
225 | break; | |
226 | default: | |
227 | addAnnotationForKeyword(modifications, deletions, existing, additions, | |
228 | line + 1 + lineEmptyCount, lastLineForKeyword); | |
229 | lastLineForKeyword = null; | |
230 | lineEmptyCount = 0; | |
231 | lastLineWhichIsNotEmpty = line; | |
232 | int indent = computeIndentLevel(lineContent, tabSize); | |
233 | if (indent == -1) { | |
234 | continue; // only whitespace | |
235 | } | |
236 | ||
237 | LineIndent previous = previousRegions.get(previousRegions.size() - 1); | |
238 | if (previous.indent > indent) { | |
239 | // discard all regions with larger indent | |
240 | do { | |
241 | previousRegions.remove(previousRegions.size() - 1); | |
242 | previous = previousRegions.get(previousRegions.size() - 1); | |
243 | } while (previous.indent > indent); | |
244 | ||
245 | // new folding range | |
246 | int endLineNumber = previous.line - 1; | |
247 | if (endLineNumber - line >= minimumRangeSize) { | |
248 | updateAnnotation(modifications, deletions, existing, additions, line, endLineNumber); | |
249 | } | |
250 | } | |
251 | if (previous.indent == indent) { | |
252 | previous.line = line; | |
253 | } else { // previous.indent < indent | |
254 | // new region with a bigger indent | |
255 | previousRegions.add(new LineIndent(line, indent)); | |
256 | } | |
257 | } | |
258 | } | |
259 | addAnnotationForKeyword(modifications, deletions, existing, additions, lastLineWhichIsNotEmpty, | |
260 | lastLineForKeyword); | |
261 | } catch (BadLocationException e) { | |
262 | // should never done | |
263 | e.printStackTrace(); | |
264 | } | |
265 | ||
266 | // be sure projection has not been disabled | |
267 | if (projectionAnnotationModel != null) { | |
268 | if (existing.size() > 0) { | |
269 | deletions.addAll(existing); | |
270 | } | |
271 | // send the calculated updates to the annotations to the | |
272 | // annotation model | |
273 | projectionAnnotationModel.modifyAnnotations(deletions.toArray(new Annotation[1]), additions, | |
274 | modifications.toArray(new Annotation[0])); | |
275 | } | |
276 | } | |
277 | } | |
278 | ||
279 | private void addAnnotationForKeyword(List<Annotation> modifications, List<FoldingAnnotation> deletions, | |
280 | List<FoldingAnnotation> existing, Map<Annotation, Position> additions, int startLine, | |
281 | Integer lastLineForKeyword) throws BadLocationException { | |
282 | if (lastLineForKeyword != null) { | |
283 | updateAnnotation(modifications, deletions, existing, additions, startLine, lastLineForKeyword); | |
284 | } | |
285 | } | |
286 | ||
287 | private enum LineState { | |
288 | StartWithKeyWord, DontStartWithKeyWord, EmptyLine | |
289 | } | |
290 | ||
291 | /** | |
292 | * Returns the line state for line which starts with a given keyword. | |
293 | * | |
294 | * @param lineContent line content. | |
295 | * @param lastLineForKeyword last line for the given keyword. | |
296 | * @return | |
297 | */ | |
298 | private LineState getLineState(String lineContent, Integer lastLineForKeyword) { | |
299 | if (lineStartsWithKeyword == null) { | |
300 | // none keyword defined. | |
301 | return LineState.DontStartWithKeyWord; | |
302 | } | |
303 | if (lineContent != null && lineContent.trim().startsWith(lineStartsWithKeyword)) { | |
304 | // The line starts with the given keyword (ex: starts with "import") | |
305 | return LineState.StartWithKeyWord; | |
306 | } | |
307 | if (lastLineForKeyword != null && (lineContent == null || lineContent.trim().length() == 0)) { | |
308 | // a last line for keyword was defined, line is empty | |
309 | return LineState.EmptyLine; | |
310 | } | |
311 | return LineState.DontStartWithKeyWord; | |
312 | } | |
313 | ||
314 | /** | |
315 | * Compute indentation level of the given line by using the given tab size. | |
316 | * | |
317 | * @param line the line text. | |
318 | * @param tabSize the tab size. | |
319 | * @return the indentation level of the given line by using the given tab size. | |
320 | */ | |
321 | private static int computeIndentLevel(String line, int tabSize) { | |
322 | int i = 0; | |
323 | int indent = 0; | |
324 | while (i < line.length()) { | |
325 | char ch = line.charAt(i); | |
326 | if (ch == ' ') { | |
327 | indent++; | |
328 | } else if (ch == '\t') { | |
329 | indent = indent - indent % tabSize + tabSize; | |
330 | } else { | |
331 | break; | |
332 | } | |
333 | i++; | |
334 | } | |
335 | if (i == line.length()) { | |
336 | return -1; // line only consists of whitespace | |
337 | } | |
338 | return indent; | |
339 | } | |
340 | ||
341 | /** | |
342 | * Given a {@link DirtyRegion} returns an {@link Iterator} of the already | |
343 | * existing annotations in that region. | |
344 | * | |
345 | * @param dirtyRegion the {@link DirtyRegion} to check for existing annotations | |
346 | * in | |
347 | * | |
348 | * @return an {@link Iterator} over the annotations in the given | |
349 | * {@link DirtyRegion}. The iterator could have no annotations in it. Or | |
350 | * <code>null</code> if projection has been disabled. | |
351 | */ | |
352 | private Iterator<Annotation> getAnnotationIterator(DirtyRegion dirtyRegion) { | |
353 | Iterator<Annotation> annoIter = null; | |
354 | // be sure project has not been disabled | |
355 | if (projectionAnnotationModel != null) { | |
356 | // workaround for Platform Bug 299416 | |
357 | annoIter = projectionAnnotationModel.getAnnotationIterator(0, document.getLength(), false, false); | |
358 | } | |
359 | return annoIter; | |
360 | } | |
361 | ||
362 | /** | |
363 | * Update annotations. | |
364 | * | |
365 | * @param modifications the folding annotations to update. | |
366 | * @param deletions the folding annotations to delete. | |
367 | * @param existing the existing folding annotations. | |
368 | * @param additions annoation to add | |
369 | * @param line the line index | |
370 | * @param endLineNumber the end line number | |
371 | * @throws BadLocationException | |
372 | */ | |
373 | private void updateAnnotation(List<Annotation> modifications, List<FoldingAnnotation> deletions, | |
374 | List<FoldingAnnotation> existing, Map<Annotation, Position> additions, int line, Integer endLineNumber) | |
375 | throws BadLocationException { | |
376 | int startOffset = document.getLineOffset(line); | |
377 | int endOffset = document.getLineOffset(endLineNumber) + document.getLineLength(endLineNumber); | |
378 | Position newPos = new Position(startOffset, endOffset - startOffset); | |
379 | if (existing.size() > 0) { | |
380 | FoldingAnnotation existingAnnotation = existing.remove(existing.size() - 1); | |
381 | updateAnnotations(existingAnnotation, newPos, modifications, deletions); | |
382 | } else { | |
383 | additions.put(new FoldingAnnotation(false), newPos); | |
384 | } | |
385 | } | |
386 | ||
387 | /** | |
388 | * Update annotations. | |
389 | * | |
390 | * @param existingAnnotation the existing annotations that need to be updated | |
391 | * based on the given dirtied IndexRegion | |
392 | * @param newPos the new position that caused the annotations need | |
393 | * for updating and null otherwise. | |
394 | * @param modifications the list of annotations to be modified | |
395 | * @param deletions the list of annotations to be deleted | |
396 | */ | |
397 | protected void updateAnnotations(Annotation existingAnnotation, Position newPos, List<Annotation> modifications, | |
398 | List<FoldingAnnotation> deletions) { | |
399 | if (existingAnnotation instanceof FoldingAnnotation) { | |
400 | FoldingAnnotation foldingAnnotation = (FoldingAnnotation) existingAnnotation; | |
401 | ||
402 | // if a new position can be calculated then update the position of | |
403 | // the annotation, | |
404 | // else the annotation needs to be deleted | |
405 | if (newPos != null && newPos.length > 0 && projectionAnnotationModel != null) { | |
406 | Position oldPos = projectionAnnotationModel.getPosition(foldingAnnotation); | |
407 | // only update the position if we have to | |
408 | if (!newPos.equals(oldPos)) { | |
409 | oldPos.setOffset(newPos.offset); | |
410 | oldPos.setLength(newPos.length); | |
411 | modifications.add(foldingAnnotation); | |
412 | } | |
413 | } else { | |
414 | deletions.add(foldingAnnotation); | |
415 | } | |
416 | } | |
417 | } | |
418 | ||
419 | /** | |
420 | * <p> | |
421 | * Searches the given {@link DirtyRegion} for annotations that now have a length | |
422 | * of 0. This is caused when something that was being folded has been deleted. | |
423 | * These {@link FoldingAnnotation}s are then added to the {@link List} of | |
424 | * {@link FoldingAnnotation}s to be deleted | |
425 | * </p> | |
426 | * | |
427 | * @param dirtyRegion find the now invalid {@link FoldingAnnotation}s in this | |
428 | * {@link DirtyRegion} | |
429 | * @param deletions the current list of {@link FoldingAnnotation}s marked for | |
430 | * deletion that the newly found invalid | |
431 | * {@link FoldingAnnotation}s will be added to | |
432 | */ | |
433 | protected void markInvalidAnnotationsForDeletion(DirtyRegion dirtyRegion, List<FoldingAnnotation> deletions, | |
434 | List<FoldingAnnotation> existing) { | |
435 | Iterator<Annotation> iter = getAnnotationIterator(dirtyRegion); | |
436 | if (iter != null) { | |
437 | while (iter.hasNext()) { | |
438 | Annotation anno = iter.next(); | |
439 | if (anno instanceof FoldingAnnotation) { | |
440 | FoldingAnnotation folding = (FoldingAnnotation) anno; | |
441 | Position pos = projectionAnnotationModel.getPosition(anno); | |
442 | if (pos.length == 0) { | |
443 | deletions.add(folding); | |
444 | } else { | |
445 | existing.add(folding); | |
446 | } | |
447 | } | |
448 | } | |
449 | } | |
450 | } | |
451 | ||
452 | @Override | |
453 | public void reconcile(IRegion partition) { | |
454 | // not used, we use: | |
455 | // reconcile(DirtyRegion dirtyRegion, IRegion subRegion) | |
456 | } | |
457 | ||
458 | @Override | |
459 | public void setProgressMonitor(IProgressMonitor monitor) { | |
460 | // Do nothing | |
461 | } | |
462 | ||
463 | @Override | |
464 | public void initialReconcile() { | |
465 | reconcile(new DirtyRegion(0, document.getLength(), DirtyRegion.INSERT, document.get()), null); | |
466 | } | |
0 | /******************************************************************************* | |
1 | * Copyright (c) 2009, 2018 IBM Corporation and others. | |
2 | * | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * https://www.eclipse.org/legal/epl-2.0/ | |
7 | * | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * IBM Corporation - initial API and implementation | |
12 | * Angelo Zerr <angelo.zerr@gmail.com> - adapt code org.eclipse.wst.sse.ui.internal.projection.AbstractStructuredFoldingStrategy to support generic indent folding strategy. | |
13 | * [generic editor] Default Code folding for generic editor should use IndentFoldingStrategy - Bug 520659 | |
14 | */ | |
15 | package org.eclipse.ui.internal.genericeditor.folding; | |
16 | ||
17 | import java.util.ArrayList; | |
18 | import java.util.HashMap; | |
19 | import java.util.Iterator; | |
20 | import java.util.List; | |
21 | import java.util.Map; | |
22 | ||
23 | import org.eclipse.core.runtime.IProgressMonitor; | |
24 | import org.eclipse.jface.text.BadLocationException; | |
25 | import org.eclipse.jface.text.IDocument; | |
26 | import org.eclipse.jface.text.IRegion; | |
27 | import org.eclipse.jface.text.Position; | |
28 | import org.eclipse.jface.text.reconciler.DirtyRegion; | |
29 | import org.eclipse.jface.text.reconciler.IReconcilingStrategy; | |
30 | import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; | |
31 | import org.eclipse.jface.text.source.Annotation; | |
32 | import org.eclipse.jface.text.source.projection.IProjectionListener; | |
33 | import org.eclipse.jface.text.source.projection.ProjectionAnnotation; | |
34 | import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; | |
35 | import org.eclipse.jface.text.source.projection.ProjectionViewer; | |
36 | import org.eclipse.swt.graphics.FontMetrics; | |
37 | import org.eclipse.swt.graphics.GC; | |
38 | import org.eclipse.swt.graphics.Rectangle; | |
39 | import org.eclipse.swt.widgets.Canvas; | |
40 | ||
41 | /** | |
42 | * Indent folding strategy to fold code by using indentation. The folding | |
43 | * strategy must be associated with a viewer for it to function. | |
44 | */ | |
45 | public class IndentFoldingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension, IProjectionListener { | |
46 | ||
47 | private IDocument document; | |
48 | private ProjectionViewer viewer; | |
49 | private ProjectionAnnotationModel projectionAnnotationModel; | |
50 | private final String lineStartsWithKeyword; | |
51 | ||
52 | public IndentFoldingStrategy() { | |
53 | this(null); | |
54 | } | |
55 | ||
56 | public IndentFoldingStrategy(String lineStartsWithKeyword) { | |
57 | this.lineStartsWithKeyword = lineStartsWithKeyword; | |
58 | } | |
59 | ||
60 | /** | |
61 | * A FoldingAnnotation is a {@link ProjectionAnnotation} it is folding and | |
62 | * overriding the paint method (in a hacky type way) to prevent one line folding | |
63 | * annotations to be drawn. | |
64 | */ | |
65 | protected class FoldingAnnotation extends ProjectionAnnotation { | |
66 | private boolean visible; /* workaround for BUG85874 */ | |
67 | ||
68 | /** | |
69 | * Creates a new FoldingAnnotation. | |
70 | * | |
71 | * @param isCollapsed true if this annotation should be collapsed, false | |
72 | * otherwise | |
73 | */ | |
74 | public FoldingAnnotation(boolean isCollapsed) { | |
75 | super(isCollapsed); | |
76 | visible = false; | |
77 | } | |
78 | ||
79 | /** | |
80 | * Does not paint hidden annotations. Annotations are hidden when they only span | |
81 | * one line. | |
82 | * | |
83 | * @see ProjectionAnnotation#paint(org.eclipse.swt.graphics.GC, | |
84 | * org.eclipse.swt.widgets.Canvas, org.eclipse.swt.graphics.Rectangle) | |
85 | */ | |
86 | @Override | |
87 | public void paint(GC gc, Canvas canvas, Rectangle rectangle) { | |
88 | /* workaround for BUG85874 */ | |
89 | /* | |
90 | * only need to check annotations that are expanded because hidden annotations | |
91 | * should never have been given the chance to collapse. | |
92 | */ | |
93 | if (!isCollapsed()) { | |
94 | // working with rectangle, so line height | |
95 | FontMetrics metrics = gc.getFontMetrics(); | |
96 | if (metrics != null) { | |
97 | // do not draw annotations that only span one line and | |
98 | // mark them as not visible | |
99 | if ((rectangle.height / metrics.getHeight()) <= 1) { | |
100 | visible = false; | |
101 | return; | |
102 | } | |
103 | } | |
104 | } | |
105 | visible = true; | |
106 | super.paint(gc, canvas, rectangle); | |
107 | } | |
108 | ||
109 | @Override | |
110 | public void markCollapsed() { | |
111 | /* workaround for BUG85874 */ | |
112 | // do not mark collapsed if annotation is not visible | |
113 | if (visible) | |
114 | super.markCollapsed(); | |
115 | } | |
116 | } | |
117 | ||
118 | /** | |
119 | * The folding strategy must be associated with a viewer for it to function | |
120 | * | |
121 | * @param viewer the viewer to associate this folding strategy with | |
122 | */ | |
123 | public void setViewer(ProjectionViewer viewer) { | |
124 | if (this.viewer != null) { | |
125 | this.viewer.removeProjectionListener(this); | |
126 | } | |
127 | this.viewer = viewer; | |
128 | this.viewer.addProjectionListener(this); | |
129 | this.projectionAnnotationModel = this.viewer.getProjectionAnnotationModel(); | |
130 | } | |
131 | ||
132 | public void uninstall() { | |
133 | setDocument(null); | |
134 | ||
135 | if (viewer != null) { | |
136 | viewer.removeProjectionListener(this); | |
137 | viewer = null; | |
138 | } | |
139 | ||
140 | projectionDisabled(); | |
141 | } | |
142 | ||
143 | @Override | |
144 | public void setDocument(IDocument document) { | |
145 | this.document = document; | |
146 | } | |
147 | ||
148 | @Override | |
149 | public void projectionDisabled() { | |
150 | projectionAnnotationModel = null; | |
151 | } | |
152 | ||
153 | @Override | |
154 | public void projectionEnabled() { | |
155 | if (viewer != null) { | |
156 | projectionAnnotationModel = viewer.getProjectionAnnotationModel(); | |
157 | } | |
158 | } | |
159 | ||
160 | private class LineIndent { | |
161 | public int line; | |
162 | public final int indent; | |
163 | ||
164 | public LineIndent(int line, int indent) { | |
165 | this.line = line; | |
166 | this.indent = indent; | |
167 | } | |
168 | } | |
169 | ||
170 | @Override | |
171 | public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { | |
172 | if (projectionAnnotationModel != null) { | |
173 | ||
174 | // these are what are passed off to the annotation model to | |
175 | // actually create and maintain the annotations | |
176 | List<Annotation> modifications = new ArrayList<Annotation>(); | |
177 | List<FoldingAnnotation> deletions = new ArrayList<FoldingAnnotation>(); | |
178 | List<FoldingAnnotation> existing = new ArrayList<FoldingAnnotation>(); | |
179 | Map<Annotation, Position> additions = new HashMap<Annotation, Position>(); | |
180 | ||
181 | // find and mark all folding annotations with length 0 for deletion | |
182 | markInvalidAnnotationsForDeletion(dirtyRegion, deletions, existing); | |
183 | ||
184 | List<LineIndent> previousRegions = new ArrayList<LineIndent>(); | |
185 | ||
186 | int tabSize = 1; | |
187 | int minimumRangeSize = 1; | |
188 | try { | |
189 | ||
190 | // Today we recompute annotation from the whole document each | |
191 | // time. | |
192 | // performance s good even with large document, but it should be | |
193 | // better to loop for only DirtyRegion (and before/after) | |
194 | // int offset = dirtyRegion.getOffset(); | |
195 | // int length = dirtyRegion.getLength(); | |
196 | // int startLine = 0; //document.getLineOfOffset(offset); | |
197 | int endLine = document.getNumberOfLines() - 1; // startLine + | |
198 | // document.getNumberOfLines(offset, | |
199 | // length) - 1; | |
200 | ||
201 | // sentinel, to make sure there's at least one entry | |
202 | previousRegions.add(new LineIndent(endLine + 1, -1)); | |
203 | ||
204 | int lastLineWhichIsNotEmpty = 0; | |
205 | int lineEmptyCount = 0; | |
206 | Integer lastLineForKeyword = null; | |
207 | int line = endLine; | |
208 | for (line = endLine; line >= 0; line--) { | |
209 | int lineOffset = document.getLineOffset(line); | |
210 | String delim = document.getLineDelimiter(line); | |
211 | int lineLength = document.getLineLength(line) - (delim != null ? delim.length() : 0); | |
212 | String lineContent = document.get(lineOffset, lineLength); | |
213 | ||
214 | LineState state = getLineState(lineContent, lastLineForKeyword); | |
215 | switch (state) { | |
216 | case StartWithKeyWord: | |
217 | lineEmptyCount = 0; | |
218 | lastLineWhichIsNotEmpty = line; | |
219 | if (lastLineForKeyword == null) { | |
220 | lastLineForKeyword = line; | |
221 | } | |
222 | break; | |
223 | case EmptyLine: | |
224 | lineEmptyCount++; | |
225 | break; | |
226 | default: | |
227 | addAnnotationForKeyword(modifications, deletions, existing, additions, | |
228 | line + 1 + lineEmptyCount, lastLineForKeyword); | |
229 | lastLineForKeyword = null; | |
230 | lineEmptyCount = 0; | |
231 | lastLineWhichIsNotEmpty = line; | |
232 | int indent = computeIndentLevel(lineContent, tabSize); | |
233 | if (indent == -1) { | |
234 | continue; // only whitespace | |
235 | } | |
236 | ||
237 | LineIndent previous = previousRegions.get(previousRegions.size() - 1); | |
238 | if (previous.indent > indent) { | |
239 | // discard all regions with larger indent | |
240 | do { | |
241 | previousRegions.remove(previousRegions.size() - 1); | |
242 | previous = previousRegions.get(previousRegions.size() - 1); | |
243 | } while (previous.indent > indent); | |
244 | ||
245 | // new folding range | |
246 | int endLineNumber = previous.line - 1; | |
247 | if (endLineNumber - line >= minimumRangeSize) { | |
248 | updateAnnotation(modifications, deletions, existing, additions, line, endLineNumber); | |
249 | } | |
250 | } | |
251 | if (previous.indent == indent) { | |
252 | previous.line = line; | |
253 | } else { // previous.indent < indent | |
254 | // new region with a bigger indent | |
255 | previousRegions.add(new LineIndent(line, indent)); | |
256 | } | |
257 | } | |
258 | } | |
259 | addAnnotationForKeyword(modifications, deletions, existing, additions, lastLineWhichIsNotEmpty, | |
260 | lastLineForKeyword); | |
261 | } catch (BadLocationException e) { | |
262 | // should never done | |
263 | e.printStackTrace(); | |
264 | } | |
265 | ||
266 | // be sure projection has not been disabled | |
267 | if (projectionAnnotationModel != null) { | |
268 | if (existing.size() > 0) { | |
269 | deletions.addAll(existing); | |
270 | } | |
271 | // send the calculated updates to the annotations to the | |
272 | // annotation model | |
273 | projectionAnnotationModel.modifyAnnotations(deletions.toArray(new Annotation[1]), additions, | |
274 | modifications.toArray(new Annotation[0])); | |
275 | } | |
276 | } | |
277 | } | |
278 | ||
279 | private void addAnnotationForKeyword(List<Annotation> modifications, List<FoldingAnnotation> deletions, | |
280 | List<FoldingAnnotation> existing, Map<Annotation, Position> additions, int startLine, | |
281 | Integer lastLineForKeyword) throws BadLocationException { | |
282 | if (lastLineForKeyword != null) { | |
283 | updateAnnotation(modifications, deletions, existing, additions, startLine, lastLineForKeyword); | |
284 | } | |
285 | } | |
286 | ||
287 | private enum LineState { | |
288 | StartWithKeyWord, DontStartWithKeyWord, EmptyLine | |
289 | } | |
290 | ||
291 | /** | |
292 | * Returns the line state for line which starts with a given keyword. | |
293 | * | |
294 | * @param lineContent line content. | |
295 | * @param lastLineForKeyword last line for the given keyword. | |
296 | * @return | |
297 | */ | |
298 | private LineState getLineState(String lineContent, Integer lastLineForKeyword) { | |
299 | if (lineStartsWithKeyword == null) { | |
300 | // none keyword defined. | |
301 | return LineState.DontStartWithKeyWord; | |
302 | } | |
303 | if (lineContent != null && lineContent.trim().startsWith(lineStartsWithKeyword)) { | |
304 | // The line starts with the given keyword (ex: starts with "import") | |
305 | return LineState.StartWithKeyWord; | |
306 | } | |
307 | if (lastLineForKeyword != null && (lineContent == null || lineContent.trim().length() == 0)) { | |
308 | // a last line for keyword was defined, line is empty | |
309 | return LineState.EmptyLine; | |
310 | } | |
311 | return LineState.DontStartWithKeyWord; | |
312 | } | |
313 | ||
314 | /** | |
315 | * Compute indentation level of the given line by using the given tab size. | |
316 | * | |
317 | * @param line the line text. | |
318 | * @param tabSize the tab size. | |
319 | * @return the indentation level of the given line by using the given tab size. | |
320 | */ | |
321 | private static int computeIndentLevel(String line, int tabSize) { | |
322 | int i = 0; | |
323 | int indent = 0; | |
324 | while (i < line.length()) { | |
325 | char ch = line.charAt(i); | |
326 | if (ch == ' ') { | |
327 | indent++; | |
328 | } else if (ch == '\t') { | |
329 | indent = indent - indent % tabSize + tabSize; | |
330 | } else { | |
331 | break; | |
332 | } | |
333 | i++; | |
334 | } | |
335 | if (i == line.length()) { | |
336 | return -1; // line only consists of whitespace | |
337 | } | |
338 | return indent; | |
339 | } | |
340 | ||
341 | /** | |
342 | * Given a {@link DirtyRegion} returns an {@link Iterator} of the already | |
343 | * existing annotations in that region. | |
344 | * | |
345 | * @param dirtyRegion the {@link DirtyRegion} to check for existing annotations | |
346 | * in | |
347 | * | |
348 | * @return an {@link Iterator} over the annotations in the given | |
349 | * {@link DirtyRegion}. The iterator could have no annotations in it. Or | |
350 | * <code>null</code> if projection has been disabled. | |
351 | */ | |
352 | private Iterator<Annotation> getAnnotationIterator(DirtyRegion dirtyRegion) { | |
353 | Iterator<Annotation> annoIter = null; | |
354 | // be sure project has not been disabled | |
355 | if (projectionAnnotationModel != null) { | |
356 | // workaround for Platform Bug 299416 | |
357 | annoIter = projectionAnnotationModel.getAnnotationIterator(0, document.getLength(), false, false); | |
358 | } | |
359 | return annoIter; | |
360 | } | |
361 | ||
362 | /** | |
363 | * Update annotations. | |
364 | * | |
365 | * @param modifications the folding annotations to update. | |
366 | * @param deletions the folding annotations to delete. | |
367 | * @param existing the existing folding annotations. | |
368 | * @param additions annoation to add | |
369 | * @param line the line index | |
370 | * @param endLineNumber the end line number | |
371 | * @throws BadLocationException | |
372 | */ | |
373 | private void updateAnnotation(List<Annotation> modifications, List<FoldingAnnotation> deletions, | |
374 | List<FoldingAnnotation> existing, Map<Annotation, Position> additions, int line, Integer endLineNumber) | |
375 | throws BadLocationException { | |
376 | int startOffset = document.getLineOffset(line); | |
377 | int endOffset = document.getLineOffset(endLineNumber) + document.getLineLength(endLineNumber); | |
378 | Position newPos = new Position(startOffset, endOffset - startOffset); | |
379 | if (existing.size() > 0) { | |
380 | FoldingAnnotation existingAnnotation = existing.remove(existing.size() - 1); | |
381 | updateAnnotations(existingAnnotation, newPos, modifications, deletions); | |
382 | } else { | |
383 | additions.put(new FoldingAnnotation(false), newPos); | |
384 | } | |
385 | } | |
386 | ||
387 | /** | |
388 | * Update annotations. | |
389 | * | |
390 | * @param existingAnnotation the existing annotations that need to be updated | |
391 | * based on the given dirtied IndexRegion | |
392 | * @param newPos the new position that caused the annotations need | |
393 | * for updating and null otherwise. | |
394 | * @param modifications the list of annotations to be modified | |
395 | * @param deletions the list of annotations to be deleted | |
396 | */ | |
397 | protected void updateAnnotations(Annotation existingAnnotation, Position newPos, List<Annotation> modifications, | |
398 | List<FoldingAnnotation> deletions) { | |
399 | if (existingAnnotation instanceof FoldingAnnotation) { | |
400 | FoldingAnnotation foldingAnnotation = (FoldingAnnotation) existingAnnotation; | |
401 | ||
402 | // if a new position can be calculated then update the position of | |
403 | // the annotation, | |
404 | // else the annotation needs to be deleted | |
405 | if (newPos != null && newPos.length > 0 && projectionAnnotationModel != null) { | |
406 | Position oldPos = projectionAnnotationModel.getPosition(foldingAnnotation); | |
407 | // only update the position if we have to | |
408 | if (!newPos.equals(oldPos)) { | |
409 | oldPos.setOffset(newPos.offset); | |
410 | oldPos.setLength(newPos.length); | |
411 | modifications.add(foldingAnnotation); | |
412 | } | |
413 | } else { | |
414 | deletions.add(foldingAnnotation); | |
415 | } | |
416 | } | |
417 | } | |
418 | ||
419 | /** | |
420 | * <p> | |
421 | * Searches the given {@link DirtyRegion} for annotations that now have a length | |
422 | * of 0. This is caused when something that was being folded has been deleted. | |
423 | * These {@link FoldingAnnotation}s are then added to the {@link List} of | |
424 | * {@link FoldingAnnotation}s to be deleted | |
425 | * </p> | |
426 | * | |
427 | * @param dirtyRegion find the now invalid {@link FoldingAnnotation}s in this | |
428 | * {@link DirtyRegion} | |
429 | * @param deletions the current list of {@link FoldingAnnotation}s marked for | |
430 | * deletion that the newly found invalid | |
431 | * {@link FoldingAnnotation}s will be added to | |
432 | */ | |
433 | protected void markInvalidAnnotationsForDeletion(DirtyRegion dirtyRegion, List<FoldingAnnotation> deletions, | |
434 | List<FoldingAnnotation> existing) { | |
435 | Iterator<Annotation> iter = getAnnotationIterator(dirtyRegion); | |
436 | if (iter != null) { | |
437 | while (iter.hasNext()) { | |
438 | Annotation anno = iter.next(); | |
439 | if (anno instanceof FoldingAnnotation) { | |
440 | FoldingAnnotation folding = (FoldingAnnotation) anno; | |
441 | Position pos = projectionAnnotationModel.getPosition(anno); | |
442 | if (pos.length == 0) { | |
443 | deletions.add(folding); | |
444 | } else { | |
445 | existing.add(folding); | |
446 | } | |
447 | } | |
448 | } | |
449 | } | |
450 | } | |
451 | ||
452 | @Override | |
453 | public void reconcile(IRegion partition) { | |
454 | // not used, we use: | |
455 | // reconcile(DirtyRegion dirtyRegion, IRegion subRegion) | |
456 | } | |
457 | ||
458 | @Override | |
459 | public void setProgressMonitor(IProgressMonitor monitor) { | |
460 | // Do nothing | |
461 | } | |
462 | ||
463 | @Override | |
464 | public void initialReconcile() { | |
465 | reconcile(new DirtyRegion(0, document.getLength(), DirtyRegion.INSERT, document.get()), null); | |
466 | } | |
467 | 467 | }⏎ |
+15
-16
13 | 13 | package org.eclipse.ui.internal.genericeditor.markers; |
14 | 14 | |
15 | 15 | import java.util.ArrayList; |
16 | import java.util.Collections; | |
16 | 17 | import java.util.List; |
17 | 18 | |
18 | 19 | import org.eclipse.core.resources.IMarker; |
37 | 38 | if (!(annotation instanceof MarkerAnnotation)) { |
38 | 39 | return false; |
39 | 40 | } |
40 | AnnotationPreference preference= EditorsUI.getAnnotationPreferenceLookup().getAnnotationPreference(annotation); | |
41 | AnnotationPreference preference = EditorsUI.getAnnotationPreferenceLookup().getAnnotationPreference(annotation); | |
41 | 42 | if (preference == null) { |
42 | 43 | return false; |
43 | 44 | } |
44 | String key= preference.getTextPreferenceKey(); | |
45 | String key = preference.getTextPreferenceKey(); | |
45 | 46 | if (key != null) { |
46 | 47 | if (!EditorsUI.getPreferenceStore().getBoolean(key)) |
47 | 48 | return false; |
48 | 49 | } else { |
49 | key= preference.getHighlightPreferenceKey(); | |
50 | key = preference.getHighlightPreferenceKey(); | |
50 | 51 | if (key == null || !EditorsUI.getPreferenceStore().getBoolean(key)) |
51 | 52 | return false; |
52 | 53 | } |
53 | 54 | return true; |
54 | 55 | } |
55 | 56 | |
56 | ||
57 | @Override | |
58 | public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { | |
57 | @Override public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { | |
59 | 58 | Object hoverInfo = getHoverInfo2(textViewer, hoverRegion); |
60 | 59 | if (hoverInfo == null) { |
61 | 60 | return null; |
63 | 62 | return hoverInfo.toString(); |
64 | 63 | } |
65 | 64 | |
66 | @Override | |
67 | public IRegion getHoverRegion(ITextViewer textViewer, int offset) { | |
65 | @Override public IRegion getHoverRegion(ITextViewer textViewer, int offset) { | |
68 | 66 | if (!(textViewer instanceof ISourceViewerExtension2)) { |
69 | 67 | return null; |
70 | 68 | } |
71 | ISourceViewerExtension2 viewer = (ISourceViewerExtension2)textViewer; | |
69 | ISourceViewerExtension2 viewer = (ISourceViewerExtension2) textViewer; | |
72 | 70 | List<MarkerAnnotation> annotations = findMarkerAnnotations(viewer, new Region(offset, 0)); |
73 | 71 | if (annotations.isEmpty()) { |
74 | 72 | return null; |
85 | 83 | return new Region(highestOffsetStart, Math.max(0, lowestOffsetEnd - highestOffsetStart)); |
86 | 84 | } |
87 | 85 | |
88 | @Override | |
89 | public List<IMarker> getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) { | |
86 | @Override public List<IMarker> getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) { | |
90 | 87 | if (!(textViewer instanceof ISourceViewerExtension2)) { |
91 | 88 | return null; |
92 | 89 | } |
93 | List<MarkerAnnotation> annotations = findMarkerAnnotations((ISourceViewerExtension2)textViewer, hoverRegion); | |
90 | List<MarkerAnnotation> annotations = findMarkerAnnotations((ISourceViewerExtension2) textViewer, hoverRegion); | |
94 | 91 | if (annotations.isEmpty()) { |
95 | 92 | return null; |
96 | 93 | } |
100 | 97 | } |
101 | 98 | return markers; |
102 | 99 | } |
103 | ||
100 | ||
104 | 101 | private static List<MarkerAnnotation> findMarkerAnnotations(ISourceViewerExtension2 viewer, IRegion region) { |
105 | 102 | List<MarkerAnnotation> res = new ArrayList<>(); |
106 | 103 | IAnnotationModel annotationModel = viewer.getVisualAnnotationModel(); |
104 | if (annotationModel == null) { | |
105 | return Collections.emptyList(); | |
106 | } | |
107 | 107 | annotationModel.getAnnotationIterator().forEachRemaining(annotation -> { |
108 | 108 | if (isIncluded(annotation)) { |
109 | 109 | Position position = annotationModel.getPosition(annotation); |
110 | 110 | if (region.getOffset() >= position.getOffset() && region.getOffset() + region.getLength() <= position.getOffset() + position.getLength()) { |
111 | res.add((MarkerAnnotation)annotation); | |
111 | res.add((MarkerAnnotation) annotation); | |
112 | 112 | } |
113 | 113 | } |
114 | 114 | }); |
115 | 115 | return res; |
116 | 116 | } |
117 | 117 | |
118 | @Override | |
119 | public IInformationControlCreator getHoverControlCreator() { | |
118 | @Override public IInformationControlCreator getHoverControlCreator() { | |
120 | 119 | return new MarkerHoverControlCreator(); |
121 | 120 | } |
122 | 121 |
+89
-89
0 | /** | |
1 | * Copyright (c) 2018 Angelo ZERR. | |
2 | * All rights reserved. This program and the accompanying materials | |
3 | * are made available under the terms of the Eclipse Public License v2.0 | |
4 | * which accompanies this distribution, and is available at | |
5 | * http://www.eclipse.org/legal/epl-v20.html | |
6 | * | |
7 | * Contributors: | |
8 | * Angelo Zerr <angelo.zerr@gmail.com> - Bug 538111 - [generic editor] Extension point for ICharacterPairMatcher | |
9 | */ | |
10 | package org.eclipse.ui.internal.genericeditor.preferences; | |
11 | ||
12 | import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; | |
13 | import org.eclipse.jface.preference.IPreferenceStore; | |
14 | import org.eclipse.jface.preference.PreferenceConverter; | |
15 | import org.eclipse.jface.resource.ColorRegistry; | |
16 | import org.eclipse.swt.graphics.RGB; | |
17 | import org.eclipse.ui.PlatformUI; | |
18 | ||
19 | /** | |
20 | * Preference initializer for Generic Editor plug-in. | |
21 | * | |
22 | * @since 1.2 | |
23 | */ | |
24 | public class GenericEditorPluginPreferenceInitializer extends AbstractPreferenceInitializer { | |
25 | ||
26 | @Override | |
27 | public void initializeDefaultPreferences() { | |
28 | IPreferenceStore store = GenericEditorPreferenceConstants.getPreferenceStore(); | |
29 | GenericEditorPreferenceConstants.initializeDefaultValues(store); | |
30 | } | |
31 | ||
32 | public static void setThemeBasedPreferences(IPreferenceStore store, boolean fireEvent) { | |
33 | ColorRegistry registry = null; | |
34 | if (PlatformUI.isWorkbenchRunning()) | |
35 | registry = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry(); | |
36 | ||
37 | setDefault(store, GenericEditorPreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR, | |
38 | findRGB(registry, IGenericEditorThemeConstants.EDITOR_MATCHING_BRACKETS_COLOR, new RGB(127, 0, 85)), | |
39 | fireEvent); | |
40 | ||
41 | } | |
42 | ||
43 | /** | |
44 | * Sets the default value and fires a property change event if necessary. | |
45 | * | |
46 | * @param store the preference store | |
47 | * @param key the preference key | |
48 | * @param newValue the new value | |
49 | * @param fireEvent <code>false</code> if no event should be fired | |
50 | * @since 1.2 | |
51 | */ | |
52 | private static void setDefault(IPreferenceStore store, String key, RGB newValue, boolean fireEvent) { | |
53 | if (!fireEvent) { | |
54 | PreferenceConverter.setDefault(store, key, newValue); | |
55 | return; | |
56 | } | |
57 | ||
58 | RGB oldValue = null; | |
59 | if (store.isDefault(key)) | |
60 | oldValue = PreferenceConverter.getDefaultColor(store, key); | |
61 | ||
62 | PreferenceConverter.setDefault(store, key, newValue); | |
63 | ||
64 | if (oldValue != null && !oldValue.equals(newValue)) | |
65 | store.firePropertyChangeEvent(key, oldValue, newValue); | |
66 | } | |
67 | ||
68 | /** | |
69 | * Returns the RGB for the given key in the given color registry. | |
70 | * | |
71 | * @param registry the color registry | |
72 | * @param key the key for the constant in the registry | |
73 | * @param defaultRGB the default RGB if no entry is found | |
74 | * @return RGB the RGB | |
75 | * @since 1.2 | |
76 | */ | |
77 | private static RGB findRGB(ColorRegistry registry, String key, RGB defaultRGB) { | |
78 | if (registry == null) | |
79 | return defaultRGB; | |
80 | ||
81 | RGB rgb = registry.getRGB(key); | |
82 | if (rgb != null) | |
83 | return rgb; | |
84 | ||
85 | return defaultRGB; | |
86 | } | |
87 | ||
88 | } | |
0 | /** | |
1 | * Copyright (c) 2018 Angelo ZERR. | |
2 | * All rights reserved. This program and the accompanying materials | |
3 | * are made available under the terms of the Eclipse Public License v2.0 | |
4 | * which accompanies this distribution, and is available at | |
5 | * http://www.eclipse.org/legal/epl-v20.html | |
6 | * | |
7 | * Contributors: | |
8 | * Angelo Zerr <angelo.zerr@gmail.com> - Bug 538111 - [generic editor] Extension point for ICharacterPairMatcher | |
9 | */ | |
10 | package org.eclipse.ui.internal.genericeditor.preferences; | |
11 | ||
12 | import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; | |
13 | import org.eclipse.jface.preference.IPreferenceStore; | |
14 | import org.eclipse.jface.preference.PreferenceConverter; | |
15 | import org.eclipse.jface.resource.ColorRegistry; | |
16 | import org.eclipse.swt.graphics.RGB; | |
17 | import org.eclipse.ui.PlatformUI; | |
18 | ||
19 | /** | |
20 | * Preference initializer for Generic Editor plug-in. | |
21 | * | |
22 | * @since 1.2 | |
23 | */ | |
24 | public class GenericEditorPluginPreferenceInitializer extends AbstractPreferenceInitializer { | |
25 | ||
26 | @Override | |
27 | public void initializeDefaultPreferences() { | |
28 | IPreferenceStore store = GenericEditorPreferenceConstants.getPreferenceStore(); | |
29 | GenericEditorPreferenceConstants.initializeDefaultValues(store); | |
30 | } | |
31 | ||
32 | public static void setThemeBasedPreferences(IPreferenceStore store, boolean fireEvent) { | |
33 | ColorRegistry registry = null; | |
34 | if (PlatformUI.isWorkbenchRunning()) | |
35 | registry = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry(); | |
36 | ||
37 | setDefault(store, GenericEditorPreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR, | |
38 | findRGB(registry, IGenericEditorThemeConstants.EDITOR_MATCHING_BRACKETS_COLOR, new RGB(127, 0, 85)), | |
39 | fireEvent); | |
40 | ||
41 | } | |
42 | ||
43 | /** | |
44 | * Sets the default value and fires a property change event if necessary. | |
45 | * | |
46 | * @param store the preference store | |
47 | * @param key the preference key | |
48 | * @param newValue the new value | |
49 | * @param fireEvent <code>false</code> if no event should be fired | |
50 | * @since 1.2 | |
51 | */ | |
52 | private static void setDefault(IPreferenceStore store, String key, RGB newValue, boolean fireEvent) { | |
53 | if (!fireEvent) { | |
54 | PreferenceConverter.setDefault(store, key, newValue); | |
55 | return; | |
56 | } | |
57 | ||
58 | RGB oldValue = null; | |
59 | if (store.isDefault(key)) | |
60 | oldValue = PreferenceConverter.getDefaultColor(store, key); | |
61 | ||
62 | PreferenceConverter.setDefault(store, key, newValue); | |
63 | ||
64 | if (oldValue != null && !oldValue.equals(newValue)) | |
65 | store.firePropertyChangeEvent(key, oldValue, newValue); | |
66 | } | |
67 | ||
68 | /** | |
69 | * Returns the RGB for the given key in the given color registry. | |
70 | * | |
71 | * @param registry the color registry | |
72 | * @param key the key for the constant in the registry | |
73 | * @param defaultRGB the default RGB if no entry is found | |
74 | * @return RGB the RGB | |
75 | * @since 1.2 | |
76 | */ | |
77 | private static RGB findRGB(ColorRegistry registry, String key, RGB defaultRGB) { | |
78 | if (registry == null) | |
79 | return defaultRGB; | |
80 | ||
81 | RGB rgb = registry.getRGB(key); | |
82 | if (rgb != null) | |
83 | return rgb; | |
84 | ||
85 | return defaultRGB; | |
86 | } | |
87 | ||
88 | } |
+106
-106
0 | /** | |
1 | * Copyright (c) 2018 Angelo ZERR. | |
2 | * All rights reserved. This program and the accompanying materials | |
3 | * are made available under the terms of the Eclipse Public License v2.0 | |
4 | * which accompanies this distribution, and is available at | |
5 | * http://www.eclipse.org/legal/epl-v20.html | |
6 | * | |
7 | * Contributors: | |
8 | * Angelo Zerr <angelo.zerr@gmail.com> - Bug 538111 - [generic editor] Extension point for ICharacterPairMatcher | |
9 | */ | |
10 | package org.eclipse.ui.internal.genericeditor.preferences; | |
11 | ||
12 | import org.eclipse.jface.preference.IPreferenceStore; | |
13 | import org.eclipse.ui.internal.genericeditor.GenericEditorPlugin; | |
14 | ||
15 | /** | |
16 | * Preference constants used in the Generic Editor preference store. Clients | |
17 | * should only read the Generic Editor preference store using these values. | |
18 | * Clients are not allowed to modify the preference store programmatically. | |
19 | * <p> | |
20 | * This class it is not intended to be instantiated or subclassed by clients. | |
21 | * </p> | |
22 | * | |
23 | * @since 1.2 | |
24 | * | |
25 | * @noinstantiate This class is not intended to be instantiated by clients. | |
26 | * @noextend This class is not intended to be subclassed by clients. | |
27 | */ | |
28 | public class GenericEditorPreferenceConstants { | |
29 | ||
30 | private GenericEditorPreferenceConstants() { | |
31 | ||
32 | } | |
33 | ||
34 | /** | |
35 | * A named preference that controls whether bracket matching highlighting is | |
36 | * turned on or off. | |
37 | * <p> | |
38 | * Value is of type <code>Boolean</code>. | |
39 | * </p> | |
40 | * | |
41 | * @since 1.2 | |
42 | */ | |
43 | public final static String EDITOR_MATCHING_BRACKETS = "matchingBrackets"; //$NON-NLS-1$ | |
44 | ||
45 | /** | |
46 | * A named preference that holds the color used to highlight matching brackets. | |
47 | * <p> | |
48 | * Value is of type <code>String</code>. A RGB color value encoded as a string | |
49 | * using class <code>PreferenceConverter</code> | |
50 | * </p> | |
51 | * | |
52 | * @see org.eclipse.jface.resource.StringConverter | |
53 | * @see org.eclipse.jface.preference.PreferenceConverter | |
54 | * | |
55 | * @since 1.2 | |
56 | */ | |
57 | public final static String EDITOR_MATCHING_BRACKETS_COLOR = "matchingBracketsColor"; //$NON-NLS-1$ | |
58 | ||
59 | /** | |
60 | * A named preference that controls whether bracket at caret location is | |
61 | * highlighted or not. | |
62 | * <p> | |
63 | * Value is of type <code>Boolean</code>. | |
64 | * </p> | |
65 | * | |
66 | * @since 1.2 | |
67 | */ | |
68 | public final static String EDITOR_HIGHLIGHT_BRACKET_AT_CARET_LOCATION = "highlightBracketAtCaretLocation"; //$NON-NLS-1$ | |
69 | ||
70 | /** | |
71 | * A named preference that controls whether enclosing bracket matching | |
72 | * highlighting is turned on or off. | |
73 | * <p> | |
74 | * Value is of type <code>Boolean</code>. | |
75 | * </p> | |
76 | * | |
77 | * @since 1.2 | |
78 | */ | |
79 | public final static String EDITOR_ENCLOSING_BRACKETS = "enclosingBrackets"; //$NON-NLS-1$ | |
80 | ||
81 | /** | |
82 | * Returns the Generic Editor preference store. | |
83 | * | |
84 | * @return the Generic Editor preference store | |
85 | */ | |
86 | public static IPreferenceStore getPreferenceStore() { | |
87 | return GenericEditorPlugin.getDefault().getPreferenceStore(); | |
88 | } | |
89 | ||
90 | /** | |
91 | * Initializes the given preference store with the default values. | |
92 | * | |
93 | * @param store the preference store to be initialized | |
94 | * | |
95 | * @since 1.2 | |
96 | */ | |
97 | public static void initializeDefaultValues(IPreferenceStore store) { | |
98 | store.setDefault(GenericEditorPreferenceConstants.EDITOR_MATCHING_BRACKETS, true); | |
99 | store.setDefault(GenericEditorPreferenceConstants.EDITOR_HIGHLIGHT_BRACKET_AT_CARET_LOCATION, false); | |
100 | store.setDefault(GenericEditorPreferenceConstants.EDITOR_ENCLOSING_BRACKETS, false); | |
101 | // Colors that are set by the current theme | |
102 | GenericEditorPluginPreferenceInitializer.setThemeBasedPreferences(store, false); | |
103 | } | |
104 | ||
105 | } | |
0 | /** | |
1 | * Copyright (c) 2018 Angelo ZERR. | |
2 | * All rights reserved. This program and the accompanying materials | |
3 | * are made available under the terms of the Eclipse Public License v2.0 | |
4 | * which accompanies this distribution, and is available at | |
5 | * http://www.eclipse.org/legal/epl-v20.html | |
6 | * | |
7 | * Contributors: | |
8 | * Angelo Zerr <angelo.zerr@gmail.com> - Bug 538111 - [generic editor] Extension point for ICharacterPairMatcher | |
9 | */ | |
10 | package org.eclipse.ui.internal.genericeditor.preferences; | |
11 | ||
12 | import org.eclipse.jface.preference.IPreferenceStore; | |
13 | import org.eclipse.ui.internal.genericeditor.GenericEditorPlugin; | |
14 | ||
15 | /** | |
16 | * Preference constants used in the Generic Editor preference store. Clients | |
17 | * should only read the Generic Editor preference store using these values. | |
18 | * Clients are not allowed to modify the preference store programmatically. | |
19 | * <p> | |
20 | * This class it is not intended to be instantiated or subclassed by clients. | |
21 | * </p> | |
22 | * | |
23 | * @since 1.2 | |
24 | * | |
25 | * @noinstantiate This class is not intended to be instantiated by clients. | |
26 | * @noextend This class is not intended to be subclassed by clients. | |
27 | */ | |
28 | public class GenericEditorPreferenceConstants { | |
29 | ||
30 | private GenericEditorPreferenceConstants() { | |
31 | ||
32 | } | |
33 | ||
34 | /** | |
35 | * A named preference that controls whether bracket matching highlighting is | |
36 | * turned on or off. | |
37 | * <p> | |
38 | * Value is of type <code>Boolean</code>. | |
39 | * </p> | |
40 | * | |
41 | * @since 1.2 | |
42 | */ | |
43 | public final static String EDITOR_MATCHING_BRACKETS = "matchingBrackets"; //$NON-NLS-1$ | |
44 | ||
45 | /** | |
46 | * A named preference that holds the color used to highlight matching brackets. | |
47 | * <p> | |
48 | * Value is of type <code>String</code>. A RGB color value encoded as a string | |
49 | * using class <code>PreferenceConverter</code> | |
50 | * </p> | |
51 | * | |
52 | * @see org.eclipse.jface.resource.StringConverter | |
53 | * @see org.eclipse.jface.preference.PreferenceConverter | |
54 | * | |
55 | * @since 1.2 | |
56 | */ | |
57 | public final static String EDITOR_MATCHING_BRACKETS_COLOR = "matchingBracketsColor"; //$NON-NLS-1$ | |
58 | ||
59 | /** | |
60 | * A named preference that controls whether bracket at caret location is | |
61 | * highlighted or not. | |
62 | * <p> | |
63 | * Value is of type <code>Boolean</code>. | |
64 | * </p> | |
65 | * | |
66 | * @since 1.2 | |
67 | */ | |
68 | public final static String EDITOR_HIGHLIGHT_BRACKET_AT_CARET_LOCATION = "highlightBracketAtCaretLocation"; //$NON-NLS-1$ | |
69 | ||
70 | /** | |
71 | * A named preference that controls whether enclosing bracket matching | |
72 | * highlighting is turned on or off. | |
73 | * <p> | |
74 | * Value is of type <code>Boolean</code>. | |
75 | * </p> | |
76 | * | |
77 | * @since 1.2 | |
78 | */ | |
79 | public final static String EDITOR_ENCLOSING_BRACKETS = "enclosingBrackets"; //$NON-NLS-1$ | |
80 | ||
81 | /** | |
82 | * Returns the Generic Editor preference store. | |
83 | * | |
84 | * @return the Generic Editor preference store | |
85 | */ | |
86 | public static IPreferenceStore getPreferenceStore() { | |
87 | return GenericEditorPlugin.getDefault().getPreferenceStore(); | |
88 | } | |
89 | ||
90 | /** | |
91 | * Initializes the given preference store with the default values. | |
92 | * | |
93 | * @param store the preference store to be initialized | |
94 | * | |
95 | * @since 1.2 | |
96 | */ | |
97 | public static void initializeDefaultValues(IPreferenceStore store) { | |
98 | store.setDefault(GenericEditorPreferenceConstants.EDITOR_MATCHING_BRACKETS, true); | |
99 | store.setDefault(GenericEditorPreferenceConstants.EDITOR_HIGHLIGHT_BRACKET_AT_CARET_LOCATION, false); | |
100 | store.setDefault(GenericEditorPreferenceConstants.EDITOR_ENCLOSING_BRACKETS, false); | |
101 | // Colors that are set by the current theme | |
102 | GenericEditorPluginPreferenceInitializer.setThemeBasedPreferences(store, false); | |
103 | } | |
104 | ||
105 | } |
+31
-31
0 | /** | |
1 | * Copyright (c) 2018 Angelo ZERR. | |
2 | * All rights reserved. This program and the accompanying materials | |
3 | * are made available under the terms of the Eclipse Public License v2.0 | |
4 | * which accompanies this distribution, and is available at | |
5 | * http://www.eclipse.org/legal/epl-v20.html | |
6 | * | |
7 | * Contributors: | |
8 | * Angelo Zerr <angelo.zerr@gmail.com> - Bug 538111 - [generic editor] Extension point for ICharacterPairMatcher | |
9 | */ | |
10 | package org.eclipse.ui.internal.genericeditor.preferences; | |
11 | ||
12 | import org.eclipse.ui.internal.genericeditor.GenericEditorPlugin; | |
13 | ||
14 | /** | |
15 | * Defines the constants used in the <code>org.eclipse.ui.themes</code> | |
16 | * extension contributed by this plug-in. | |
17 | * | |
18 | * @since 1.2 | |
19 | */ | |
20 | public interface IGenericEditorThemeConstants { | |
21 | ||
22 | String ID_PREFIX = GenericEditorPlugin.BUNDLE_ID + "."; //$NON-NLS-1$ | |
23 | ||
24 | /** | |
25 | * Theme constant for the color used to highlight matching brackets. | |
26 | */ | |
27 | public final String EDITOR_MATCHING_BRACKETS_COLOR = ID_PREFIX | |
28 | + GenericEditorPreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR; | |
29 | ||
30 | } | |
0 | /** | |
1 | * Copyright (c) 2018 Angelo ZERR. | |
2 | * All rights reserved. This program and the accompanying materials | |
3 | * are made available under the terms of the Eclipse Public License v2.0 | |
4 | * which accompanies this distribution, and is available at | |
5 | * http://www.eclipse.org/legal/epl-v20.html | |
6 | * | |
7 | * Contributors: | |
8 | * Angelo Zerr <angelo.zerr@gmail.com> - Bug 538111 - [generic editor] Extension point for ICharacterPairMatcher | |
9 | */ | |
10 | package org.eclipse.ui.internal.genericeditor.preferences; | |
11 | ||
12 | import org.eclipse.ui.internal.genericeditor.GenericEditorPlugin; | |
13 | ||
14 | /** | |
15 | * Defines the constants used in the <code>org.eclipse.ui.themes</code> | |
16 | * extension contributed by this plug-in. | |
17 | * | |
18 | * @since 1.2 | |
19 | */ | |
20 | public interface IGenericEditorThemeConstants { | |
21 | ||
22 | String ID_PREFIX = GenericEditorPlugin.BUNDLE_ID + "."; //$NON-NLS-1$ | |
23 | ||
24 | /** | |
25 | * Theme constant for the color used to highlight matching brackets. | |
26 | */ | |
27 | public final String EDITOR_MATCHING_BRACKETS_COLOR = ID_PREFIX | |
28 | + GenericEditorPreferenceConstants.EDITOR_MATCHING_BRACKETS_COLOR; | |
29 | ||
30 | } |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: Examples for Generic Editor |
3 | 3 | Bundle-SymbolicName: org.eclipse.ui.genericeditor.examples;singleton:=true |
4 | Bundle-Version: 1.1.200.qualifier | |
4 | Bundle-Version: 1.1.300.qualifier | |
5 | 5 | Bundle-Vendor: Eclipse.org |
6 | 6 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
7 | 7 | Require-Bundle: org.eclipse.ui.genericeditor;bundle-version="1.0.0", |
13 | 13 | <parent> |
14 | 14 | <artifactId>eclipse.platform.text</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | </parent> |
18 | 18 | <groupId>org.eclipse.ui</groupId> |
19 | 19 | <artifactId>org.eclipse.ui.genericeditor.examples</artifactId> |
20 | <version>1.1.200-SNAPSHOT</version> | |
20 | <version>1.1.300-SNAPSHOT</version> | |
21 | 21 | <packaging>eclipse-plugin</packaging> |
22 | 22 | </project> |
+12
-12
25 | 25 | |
26 | 26 | public class BlueTagsPresentationReconciler extends PresentationReconciler { |
27 | 27 | |
28 | private final TextAttribute tagAttribute = new TextAttribute(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)); | |
29 | private final TextAttribute headerAttribute = new TextAttribute(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY)); | |
28 | private final TextAttribute tagAttribute = new TextAttribute(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE)); | |
29 | private final TextAttribute headerAttribute = new TextAttribute(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY)); | |
30 | 30 | |
31 | public BlueTagsPresentationReconciler() { | |
32 | RuleBasedScanner scanner= new RuleBasedScanner(); | |
33 | IRule[] rules = new IRule[2]; | |
34 | rules[1]= new SingleLineRule("<", ">", new Token(tagAttribute)); | |
35 | rules[0]= new SingleLineRule("<?", "?>", new Token(headerAttribute)); | |
36 | scanner.setRules(rules); | |
37 | DefaultDamagerRepairer dr= new DefaultDamagerRepairer(scanner); | |
38 | this.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); | |
39 | this.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); | |
40 | } | |
31 | public BlueTagsPresentationReconciler() { | |
32 | RuleBasedScanner scanner= new RuleBasedScanner(); | |
33 | IRule[] rules = new IRule[2]; | |
34 | rules[1]= new SingleLineRule("<", ">", new Token(tagAttribute)); | |
35 | rules[0]= new SingleLineRule("<?", "?>", new Token(headerAttribute)); | |
36 | scanner.setRules(rules); | |
37 | DefaultDamagerRepairer dr= new DefaultDamagerRepairer(scanner); | |
38 | this.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); | |
39 | this.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); | |
40 | } | |
41 | 41 | }⏎ |
+9
-9
22 | 22 | private FoldingStrategy fStrategy; |
23 | 23 | |
24 | 24 | public FoldingReconciler() { |
25 | fStrategy = new FoldingStrategy(); | |
26 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
27 | } | |
25 | fStrategy = new FoldingStrategy(); | |
26 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
27 | } | |
28 | 28 | |
29 | @Override | |
30 | public void install(ITextViewer textViewer) { | |
31 | super.install(textViewer); | |
32 | ProjectionViewer pViewer =(ProjectionViewer)textViewer; | |
33 | fStrategy.setProjectionViewer(pViewer); | |
34 | } | |
29 | @Override | |
30 | public void install(ITextViewer textViewer) { | |
31 | super.install(textViewer); | |
32 | ProjectionViewer pViewer =(ProjectionViewer)textViewer; | |
33 | fStrategy.setProjectionViewer(pViewer); | |
34 | } | |
35 | 35 | }⏎ |
+3
-3
21 | 21 | private HighlightStrategy fStrategy; |
22 | 22 | |
23 | 23 | public HighlightReconciler() { |
24 | fStrategy = new HighlightStrategy(); | |
25 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
26 | } | |
24 | fStrategy = new HighlightStrategy(); | |
25 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
26 | } | |
27 | 27 | |
28 | 28 | @Override |
29 | 29 | public void install(ITextViewer textViewer) { |
+23
-23
22 | 22 | import org.eclipse.jface.text.Region; |
23 | 23 | public class NatureLabelHoverProvider implements ITextHover { |
24 | 24 | |
25 | public NatureLabelHoverProvider() { | |
26 | } | |
25 | public NatureLabelHoverProvider() { | |
26 | } | |
27 | 27 | |
28 | @Override | |
29 | public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { | |
28 | @Override | |
29 | public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { | |
30 | 30 | |
31 | String contents= textViewer.getDocument().get(); | |
32 | int offset= hoverRegion.getOffset(); | |
33 | int endIndex= contents.indexOf("</nature>", offset); | |
34 | if (endIndex==-1) return ""; | |
35 | int startIndex= contents.substring(0, offset).lastIndexOf("<nature>"); | |
36 | if (startIndex==-1) return ""; | |
37 | String selection = contents.substring(startIndex+"<nature>".length(), endIndex); | |
31 | String contents= textViewer.getDocument().get(); | |
32 | int offset= hoverRegion.getOffset(); | |
33 | int endIndex= contents.indexOf("</nature>", offset); | |
34 | if (endIndex==-1) return ""; | |
35 | int startIndex= contents.substring(0, offset).lastIndexOf("<nature>"); | |
36 | if (startIndex==-1) return ""; | |
37 | String selection = contents.substring(startIndex+"<nature>".length(), endIndex); | |
38 | 38 | |
39 | IWorkspace workspace = ResourcesPlugin.getWorkspace(); | |
40 | IProjectNatureDescriptor[] natureDescriptors= workspace.getNatureDescriptors(); | |
41 | for (int i= 0; i < natureDescriptors.length; i++) { | |
42 | if (natureDescriptors[i].getNatureId().equals(selection)) | |
43 | return natureDescriptors[i].getLabel(); | |
44 | } | |
45 | return null; | |
46 | } | |
39 | IWorkspace workspace = ResourcesPlugin.getWorkspace(); | |
40 | IProjectNatureDescriptor[] natureDescriptors= workspace.getNatureDescriptors(); | |
41 | for (int i= 0; i < natureDescriptors.length; i++) { | |
42 | if (natureDescriptors[i].getNatureId().equals(selection)) | |
43 | return natureDescriptors[i].getLabel(); | |
44 | } | |
45 | return null; | |
46 | } | |
47 | 47 | |
48 | @Override | |
49 | public IRegion getHoverRegion(ITextViewer textViewer, int offset) { | |
50 | return new Region(offset, 0); | |
51 | } | |
48 | @Override | |
49 | public IRegion getHoverRegion(ITextViewer textViewer, int offset) { | |
50 | return new Region(offset, 0); | |
51 | } | |
52 | 52 | }⏎ |
+46
-46
29 | 29 | // TODO Auto-generated constructor stub |
30 | 30 | } |
31 | 31 | |
32 | @Override | |
33 | public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { | |
34 | String text = viewer.getDocument().get(); | |
35 | String natureTag= "<nature>"; | |
36 | String projectReferenceTag="<project>"; | |
37 | IWorkspace workspace = ResourcesPlugin.getWorkspace(); | |
38 | int natureTagLength = natureTag.length(); | |
32 | @Override | |
33 | public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { | |
34 | String text = viewer.getDocument().get(); | |
35 | String natureTag= "<nature>"; | |
36 | String projectReferenceTag="<project>"; | |
37 | IWorkspace workspace = ResourcesPlugin.getWorkspace(); | |
38 | int natureTagLength = natureTag.length(); | |
39 | 39 | if (text.length() >= natureTagLength && offset >= natureTagLength && text.substring(offset - natureTagLength, offset).equals(natureTag)) { |
40 | IProjectNatureDescriptor[] natureDescriptors= workspace.getNatureDescriptors(); | |
41 | ICompletionProposal[] proposals = new ICompletionProposal[natureDescriptors.length]; | |
42 | for (int i= 0; i < natureDescriptors.length; i++) { | |
43 | IProjectNatureDescriptor descriptor= natureDescriptors[i]; | |
44 | proposals[i] = new CompletionProposal(descriptor.getNatureId(), offset, 0, descriptor.getNatureId().length()); | |
45 | } | |
46 | return proposals; | |
47 | } | |
48 | int projectReferenceTagLength = projectReferenceTag.length(); | |
40 | IProjectNatureDescriptor[] natureDescriptors= workspace.getNatureDescriptors(); | |
41 | ICompletionProposal[] proposals = new ICompletionProposal[natureDescriptors.length]; | |
42 | for (int i= 0; i < natureDescriptors.length; i++) { | |
43 | IProjectNatureDescriptor descriptor= natureDescriptors[i]; | |
44 | proposals[i] = new CompletionProposal(descriptor.getNatureId(), offset, 0, descriptor.getNatureId().length()); | |
45 | } | |
46 | return proposals; | |
47 | } | |
48 | int projectReferenceTagLength = projectReferenceTag.length(); | |
49 | 49 | if (text.length() >= projectReferenceTagLength && offset >= projectReferenceTagLength && text.substring(offset - projectReferenceTagLength, offset).equals(projectReferenceTag)) { |
50 | IProject[] projects= workspace.getRoot().getProjects(); | |
51 | //TODO - filter out the project this file is in | |
52 | ICompletionProposal[] proposals = new ICompletionProposal[projects.length]; | |
53 | for (int i= 0; i < projects.length; i++) { | |
54 | proposals[i]=new CompletionProposal(projects[i].getName(), offset, 0, projects[i].getName().length()); | |
55 | } | |
56 | return proposals; | |
57 | } | |
58 | return new ICompletionProposal[0]; | |
59 | } | |
50 | IProject[] projects= workspace.getRoot().getProjects(); | |
51 | //TODO - filter out the project this file is in | |
52 | ICompletionProposal[] proposals = new ICompletionProposal[projects.length]; | |
53 | for (int i= 0; i < projects.length; i++) { | |
54 | proposals[i]=new CompletionProposal(projects[i].getName(), offset, 0, projects[i].getName().length()); | |
55 | } | |
56 | return proposals; | |
57 | } | |
58 | return new ICompletionProposal[0]; | |
59 | } | |
60 | 60 | |
61 | @Override | |
62 | public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { | |
63 | return null; | |
64 | } | |
61 | @Override | |
62 | public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { | |
63 | return null; | |
64 | } | |
65 | 65 | |
66 | @Override | |
67 | public char[] getCompletionProposalAutoActivationCharacters() { | |
68 | return null; | |
69 | } | |
66 | @Override | |
67 | public char[] getCompletionProposalAutoActivationCharacters() { | |
68 | return null; | |
69 | } | |
70 | 70 | |
71 | @Override | |
72 | public char[] getContextInformationAutoActivationCharacters() { | |
73 | return null; | |
74 | } | |
71 | @Override | |
72 | public char[] getContextInformationAutoActivationCharacters() { | |
73 | return null; | |
74 | } | |
75 | 75 | |
76 | @Override | |
77 | public String getErrorMessage() { | |
78 | return null; | |
79 | } | |
76 | @Override | |
77 | public String getErrorMessage() { | |
78 | return null; | |
79 | } | |
80 | 80 | |
81 | @Override | |
82 | public IContextInformationValidator getContextInformationValidator() { | |
83 | return null; | |
84 | } | |
81 | @Override | |
82 | public IContextInformationValidator getContextInformationValidator() { | |
83 | return null; | |
84 | } | |
85 | 85 | |
86 | 86 | }⏎ |
+21
-21
0 | /** | |
1 | * Copyright (c) 2018 Angelo ZERR. | |
2 | * All rights reserved. This program and the accompanying materials | |
3 | * are made available under the terms of the Eclipse Public License v2.0 | |
4 | * which accompanies this distribution, and is available at | |
5 | * http://www.eclipse.org/legal/epl-v20.html | |
6 | * | |
7 | * Contributors: | |
8 | * Angelo Zerr <angelo.zerr@gmail.com> - Bug 538111 - [generic editor] Extension point for ICharacterPairMatcher | |
9 | */ | |
10 | package org.eclipse.ui.genericeditor.examples.dotproject; | |
11 | ||
12 | import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; | |
13 | ||
14 | public class TagCharacterPairMatcher extends DefaultCharacterPairMatcher { | |
15 | ||
16 | public TagCharacterPairMatcher() { | |
17 | super(new char[] { '<', '>', '"', '"' }); | |
18 | } | |
19 | ||
20 | } | |
0 | /** | |
1 | * Copyright (c) 2018 Angelo ZERR. | |
2 | * All rights reserved. This program and the accompanying materials | |
3 | * are made available under the terms of the Eclipse Public License v2.0 | |
4 | * which accompanies this distribution, and is available at | |
5 | * http://www.eclipse.org/legal/epl-v20.html | |
6 | * | |
7 | * Contributors: | |
8 | * Angelo Zerr <angelo.zerr@gmail.com> - Bug 538111 - [generic editor] Extension point for ICharacterPairMatcher | |
9 | */ | |
10 | package org.eclipse.ui.genericeditor.examples.dotproject; | |
11 | ||
12 | import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; | |
13 | ||
14 | public class TagCharacterPairMatcher extends DefaultCharacterPairMatcher { | |
15 | ||
16 | public TagCharacterPairMatcher() { | |
17 | super(new char[] { '<', '>', '"', '"' }); | |
18 | } | |
19 | ||
20 | } |
+46
-46
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide extension point for CodeMining - Bug 528419 | |
12 | */ | |
13 | package org.eclipse.ui.genericeditor.examples.dotproject.codemining; | |
14 | ||
15 | import java.util.concurrent.CompletableFuture; | |
16 | ||
17 | import org.eclipse.core.resources.IProject; | |
18 | import org.eclipse.core.resources.ResourcesPlugin; | |
19 | import org.eclipse.core.runtime.IProgressMonitor; | |
20 | import org.eclipse.jface.text.BadLocationException; | |
21 | import org.eclipse.jface.text.IDocument; | |
22 | import org.eclipse.jface.text.ITextViewer; | |
23 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
24 | import org.eclipse.jface.text.codemining.LineHeaderCodeMining; | |
25 | ||
26 | /** | |
27 | * Project reference mining. | |
28 | */ | |
29 | public class ProjectReferenceCodeMining extends LineHeaderCodeMining { | |
30 | ||
31 | private final String projectName; | |
32 | ||
33 | public ProjectReferenceCodeMining(String projectName, int beforeLineNumber, IDocument document, | |
34 | ICodeMiningProvider provider) throws BadLocationException { | |
35 | super(beforeLineNumber, document, provider); | |
36 | this.projectName = projectName; | |
37 | } | |
38 | ||
39 | @Override | |
40 | protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { | |
41 | return CompletableFuture.runAsync(() -> { | |
42 | IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); | |
43 | int refCount = project != null ? project.getReferencingProjects().length : 0; | |
44 | super.setLabel(refCount + (refCount > 1 ? " references" : " reference")); | |
45 | }); | |
46 | } | |
47 | ||
48 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide extension point for CodeMining - Bug 528419 | |
12 | */ | |
13 | package org.eclipse.ui.genericeditor.examples.dotproject.codemining; | |
14 | ||
15 | import java.util.concurrent.CompletableFuture; | |
16 | ||
17 | import org.eclipse.core.resources.IProject; | |
18 | import org.eclipse.core.resources.ResourcesPlugin; | |
19 | import org.eclipse.core.runtime.IProgressMonitor; | |
20 | import org.eclipse.jface.text.BadLocationException; | |
21 | import org.eclipse.jface.text.IDocument; | |
22 | import org.eclipse.jface.text.ITextViewer; | |
23 | import org.eclipse.jface.text.codemining.ICodeMiningProvider; | |
24 | import org.eclipse.jface.text.codemining.LineHeaderCodeMining; | |
25 | ||
26 | /** | |
27 | * Project reference mining. | |
28 | */ | |
29 | public class ProjectReferenceCodeMining extends LineHeaderCodeMining { | |
30 | ||
31 | private final String projectName; | |
32 | ||
33 | public ProjectReferenceCodeMining(String projectName, int beforeLineNumber, IDocument document, | |
34 | ICodeMiningProvider provider) throws BadLocationException { | |
35 | super(beforeLineNumber, document, provider); | |
36 | this.projectName = projectName; | |
37 | } | |
38 | ||
39 | @Override | |
40 | protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { | |
41 | return CompletableFuture.runAsync(() -> { | |
42 | IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); | |
43 | int refCount = project != null ? project.getReferencingProjects().length : 0; | |
44 | super.setLabel(refCount + (refCount > 1 ? " references" : " reference")); | |
45 | }); | |
46 | } | |
47 | ||
48 | } |
+75
-75
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
0 | /** | |
1 | * Copyright (c) 2017 Angelo ZERR. | |
2 | 2 | * |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
3 | * This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License 2.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | 6 | * https://www.eclipse.org/legal/epl-2.0/ |
7 | 7 | * |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide extension point for CodeMining - Bug 528419 | |
12 | */ | |
13 | package org.eclipse.ui.genericeditor.examples.dotproject.codemining; | |
14 | ||
15 | import java.util.ArrayList; | |
16 | import java.util.List; | |
17 | import java.util.concurrent.CompletableFuture; | |
18 | ||
19 | import org.eclipse.core.runtime.IProgressMonitor; | |
20 | import org.eclipse.jface.text.BadLocationException; | |
21 | import org.eclipse.jface.text.IDocument; | |
22 | import org.eclipse.jface.text.ITextViewer; | |
23 | import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; | |
24 | import org.eclipse.jface.text.codemining.ICodeMining; | |
25 | ||
26 | /** | |
27 | * Project reference minings provider. | |
28 | */ | |
29 | public class ProjectReferencesCodeMiningProvider extends AbstractCodeMiningProvider { | |
30 | ||
31 | @Override | |
32 | public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, | |
33 | IProgressMonitor monitor) { | |
34 | return CompletableFuture.supplyAsync(() -> { | |
35 | IDocument document = viewer.getDocument(); | |
36 | List<ICodeMining> minings = new ArrayList<>(); | |
37 | int lineCount = document.getNumberOfLines(); | |
38 | for (int i = 0; i < lineCount; i++) { | |
39 | // check if request was canceled. | |
40 | monitor.isCanceled(); | |
41 | String line = getLineText(document, i).trim(); | |
42 | int startIndex = line.indexOf("<name>"); | |
43 | if (startIndex != -1) { | |
44 | // It's the first name, we consider we are in <projectDescription></name> | |
45 | startIndex += "<name>".length(); | |
46 | int endIndex = line.indexOf("</name>"); | |
47 | if (endIndex > startIndex) { | |
48 | // Check if parent element is projectDescription | |
49 | String projectName = line.substring(startIndex, endIndex); | |
50 | if (projectName.length() > 0) { | |
51 | try { | |
52 | minings.add(new ProjectReferenceCodeMining(projectName, i, document, this)); | |
53 | } catch (BadLocationException e) { | |
54 | e.printStackTrace(); | |
55 | } | |
56 | } | |
57 | } | |
58 | // stop the compute of minings to avoid process other name like | |
59 | // <buildCommand><name> | |
60 | break; | |
61 | } | |
62 | } | |
63 | return minings; | |
64 | }); | |
65 | } | |
66 | ||
67 | private static String getLineText(IDocument document, int line) { | |
68 | try { | |
69 | int lo = document.getLineOffset(line); | |
70 | int ll = document.getLineLength(line); | |
71 | return document.get(lo, ll); | |
72 | } catch (Exception e) { | |
73 | e.printStackTrace(); | |
74 | return null; | |
75 | } | |
76 | } | |
77 | } | |
8 | * SPDX-License-Identifier: EPL-2.0 | |
9 | * | |
10 | * Contributors: | |
11 | * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide extension point for CodeMining - Bug 528419 | |
12 | */ | |
13 | package org.eclipse.ui.genericeditor.examples.dotproject.codemining; | |
14 | ||
15 | import java.util.ArrayList; | |
16 | import java.util.List; | |
17 | import java.util.concurrent.CompletableFuture; | |
18 | ||
19 | import org.eclipse.core.runtime.IProgressMonitor; | |
20 | import org.eclipse.jface.text.BadLocationException; | |
21 | import org.eclipse.jface.text.IDocument; | |
22 | import org.eclipse.jface.text.ITextViewer; | |
23 | import org.eclipse.jface.text.codemining.AbstractCodeMiningProvider; | |
24 | import org.eclipse.jface.text.codemining.ICodeMining; | |
25 | ||
26 | /** | |
27 | * Project reference minings provider. | |
28 | */ | |
29 | public class ProjectReferencesCodeMiningProvider extends AbstractCodeMiningProvider { | |
30 | ||
31 | @Override | |
32 | public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, | |
33 | IProgressMonitor monitor) { | |
34 | return CompletableFuture.supplyAsync(() -> { | |
35 | IDocument document = viewer.getDocument(); | |
36 | List<ICodeMining> minings = new ArrayList<>(); | |
37 | int lineCount = document.getNumberOfLines(); | |
38 | for (int i = 0; i < lineCount; i++) { | |
39 | // check if request was canceled. | |
40 | monitor.isCanceled(); | |
41 | String line = getLineText(document, i).trim(); | |
42 | int startIndex = line.indexOf("<name>"); | |
43 | if (startIndex != -1) { | |
44 | // It's the first name, we consider we are in <projectDescription></name> | |
45 | startIndex += "<name>".length(); | |
46 | int endIndex = line.indexOf("</name>"); | |
47 | if (endIndex > startIndex) { | |
48 | // Check if parent element is projectDescription | |
49 | String projectName = line.substring(startIndex, endIndex); | |
50 | if (projectName.length() > 0) { | |
51 | try { | |
52 | minings.add(new ProjectReferenceCodeMining(projectName, i, document, this)); | |
53 | } catch (BadLocationException e) { | |
54 | e.printStackTrace(); | |
55 | } | |
56 | } | |
57 | } | |
58 | // stop the compute of minings to avoid process other name like | |
59 | // <buildCommand><name> | |
60 | break; | |
61 | } | |
62 | } | |
63 | return minings; | |
64 | }); | |
65 | } | |
66 | ||
67 | private static String getLineText(IDocument document, int line) { | |
68 | try { | |
69 | int lo = document.getLineOffset(line); | |
70 | int ll = document.getLineLength(line); | |
71 | return document.get(lo, ll); | |
72 | } catch (Exception e) { | |
73 | e.printStackTrace(); | |
74 | return null; | |
75 | } | |
76 | } | |
77 | } |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %Plugin.name |
3 | 3 | Bundle-SymbolicName: org.eclipse.ui.genericeditor.tests;singleton:=true |
4 | Bundle-Version: 1.1.200.qualifier | |
4 | Bundle-Version: 1.1.300.qualifier | |
5 | 5 | Bundle-Vendor: %Plugin.providerName |
6 | 6 | Bundle-Localization: plugin |
7 | 7 | Export-Package: org.eclipse.ui.genericeditor.tests, |
13 | 13 | <parent> |
14 | 14 | <artifactId>tests-pom</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | <relativePath>../tests-pom/</relativePath> |
18 | 18 | </parent> |
19 | 19 | <groupId>org.eclipse.ui</groupId> |
20 | 20 | <artifactId>org.eclipse.ui.genericeditor.tests</artifactId> |
21 | <version>1.1.200-SNAPSHOT</version> | |
21 | <version>1.1.300-SNAPSHOT</version> | |
22 | 22 | <packaging>eclipse-test-plugin</packaging> |
23 | 23 | <properties> |
24 | 24 | <testSuite>${project.artifactId}</testSuite> |
+9
-9
22 | 22 | private FoldingStrategy fStrategy; |
23 | 23 | |
24 | 24 | public FoldingReconciler() { |
25 | fStrategy = new FoldingStrategy(); | |
26 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
27 | } | |
25 | fStrategy = new FoldingStrategy(); | |
26 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
27 | } | |
28 | 28 | |
29 | @Override | |
30 | public void install(ITextViewer textViewer) { | |
31 | super.install(textViewer); | |
32 | ProjectionViewer pViewer =(ProjectionViewer)textViewer; | |
33 | fStrategy.setProjectionViewer(pViewer); | |
34 | } | |
29 | @Override | |
30 | public void install(ITextViewer textViewer) { | |
31 | super.install(textViewer); | |
32 | ProjectionViewer pViewer =(ProjectionViewer)textViewer; | |
33 | fStrategy.setProjectionViewer(pViewer); | |
34 | } | |
35 | 35 | }⏎ |
+3
-3
21 | 21 | private HighlightStrategy fStrategy; |
22 | 22 | |
23 | 23 | public HighlightReconciler() { |
24 | fStrategy = new HighlightStrategy(); | |
25 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
26 | } | |
24 | fStrategy = new HighlightStrategy(); | |
25 | this.setReconcilingStrategy(fStrategy, IDocument.DEFAULT_CONTENT_TYPE); | |
26 | } | |
27 | 27 | |
28 | 28 | @Override |
29 | 29 | public void install(ITextViewer textViewer) { |
+1
-1
17 | 17 | |
18 | 18 | public class TheReconcilerFirst extends Reconciler{ |
19 | 19 | public TheReconcilerFirst() { |
20 | this.setReconcilingStrategy(new ReconcilerStrategyFirst(), IDocument.DEFAULT_CONTENT_TYPE); | |
20 | this.setReconcilingStrategy(new ReconcilerStrategyFirst(), IDocument.DEFAULT_CONTENT_TYPE); | |
21 | 21 | } |
22 | 22 | } |
+1
-1
17 | 17 | |
18 | 18 | public class TheReconcilerSecond extends Reconciler{ |
19 | 19 | public TheReconcilerSecond() { |
20 | this.setReconcilingStrategy(new ReconcilerStrategySecond(), IDocument.DEFAULT_CONTENT_TYPE); | |
20 | this.setReconcilingStrategy(new ReconcilerStrategySecond(), IDocument.DEFAULT_CONTENT_TYPE); | |
21 | 21 | } |
22 | 22 | } |
0 | 0 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> |
1 | 1 | <component id="org.eclipse.ui.workbench.texteditor" version="2"> |
2 | <resource path="META-INF/MANIFEST.MF"> | |
3 | <filter comment="Filter for bug 515570 - [api] org.eclipse.ui.editorActions should allow modifiers" id="926941240"> | |
4 | <message_arguments> | |
5 | <message_argument value="3.12.0"/> | |
6 | <message_argument value="3.11.300"/> | |
7 | </message_arguments> | |
8 | </filter> | |
9 | </resource> | |
2 | 10 | <resource path="src/org/eclipse/ui/texteditor/ITextEditorActionConstants.java" type="org.eclipse.ui.texteditor.ITextEditorActionConstants"> |
3 | 11 | <filter id="571473929"> |
4 | 12 | <message_arguments> |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %pluginName |
3 | 3 | Bundle-SymbolicName: org.eclipse.ui.workbench.texteditor; singleton:=true |
4 | Bundle-Version: 3.11.300.qualifier | |
4 | Bundle-Version: 3.12.0.qualifier | |
5 | 5 | Bundle-Activator: org.eclipse.ui.internal.texteditor.TextEditorPlugin |
6 | 6 | Bundle-ActivationPolicy: lazy |
7 | 7 | Bundle-Vendor: %providerName |
1298 | 1298 | </fontValue> |
1299 | 1299 | <fontValue |
1300 | 1300 | os="macosx" |
1301 | value="Monaco-regular-11"> | |
1301 | value="Menlo-regular-12"> | |
1302 | 1302 | </fontValue> |
1303 | 1303 | </fontDefinition> |
1304 | 1304 | <colorDefinition |
0 | 0 | <?xml version="1.0" encoding="UTF-8"?> |
1 | 1 | <!-- |
2 | Copyright (c) 2012, 2018 Eclipse Foundation and others. | |
2 | Copyright (c) 2012, 2019 Eclipse Foundation and others. | |
3 | 3 | All rights reserved. This program and the accompanying materials |
4 | 4 | are made available under the terms of the Eclipse Distribution License v1.0 |
5 | 5 | which accompanies this distribution, and is available at |
13 | 13 | <parent> |
14 | 14 | <artifactId>eclipse.platform.text</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | </parent> |
18 | 18 | <groupId>org.eclipse.ui</groupId> |
19 | 19 | <artifactId>org.eclipse.ui.workbench.texteditor</artifactId> |
20 | <version>3.11.300-SNAPSHOT</version> | |
20 | <version>3.12.0-SNAPSHOT</version> | |
21 | 21 | <packaging>eclipse-plugin</packaging> |
22 | 22 | </project> |
+3
-3
161 | 161 | |
162 | 162 | /** |
163 | 163 | * Search for possible completions in the backward direction. If there |
164 | * is a possible completion that begins before <code>firstPosition</code> | |
165 | * but ends after that position, it will not be included in the results. | |
164 | * is a possible completion that begins before <code>firstPosition</code> | |
165 | * but ends after that position, it will not be included in the results. | |
166 | 166 | * |
167 | 167 | * @param document the document to be scanned |
168 | 168 | * @param prefix the completion prefix |
169 | 169 | * @param firstPosition the caret position |
170 | 170 | * @return a {@link List} of possible completions ({@link String}s) |
171 | 171 | * from the caret position to the beginning of the document. |
172 | * The empty suggestion is not included in the results. | |
172 | * The empty suggestion is not included in the results. | |
173 | 173 | * @throws BadLocationException if any error occurs |
174 | 174 | */ |
175 | 175 | public List<String> getCompletionsBackwards(IDocument document, CharSequence prefix, int firstPosition) throws BadLocationException { |
+7
-7
1415 | 1415 | fRightDocument= document; |
1416 | 1416 | fRightDocument.addDocumentListener(this); |
1417 | 1417 | if (document instanceof IDocumentExtension4) { |
1418 | IDocumentExtension4 ext= (IDocumentExtension4) document; | |
1419 | ext.addDocumentRewriteSessionListener(fSessionListener); | |
1420 | } | |
1418 | IDocumentExtension4 ext= (IDocumentExtension4) document; | |
1419 | ext.addDocumentRewriteSessionListener(fSessionListener); | |
1420 | } | |
1421 | 1421 | initialize(); |
1422 | 1422 | } |
1423 | 1423 | } |
1453 | 1453 | if (fRightDocument != null) { |
1454 | 1454 | fRightDocument.removeDocumentListener(this); |
1455 | 1455 | if (fRightDocument instanceof IDocumentExtension4) { |
1456 | IDocumentExtension4 ext= (IDocumentExtension4) fRightDocument; | |
1457 | ext.removeDocumentRewriteSessionListener(fSessionListener); | |
1458 | } | |
1456 | IDocumentExtension4 ext= (IDocumentExtension4) fRightDocument; | |
1457 | ext.removeDocumentRewriteSessionListener(fSessionListener); | |
1458 | } | |
1459 | 1459 | } |
1460 | 1460 | fRightDocument= null; |
1461 | 1461 | fRightEquivalent= null; |
1485 | 1485 | List<QuickDiffRangeDifference> differences= fDifferences; // atomic |
1486 | 1486 | synchronized (differences) { |
1487 | 1487 | copy= new ArrayList<>(differences); |
1488 | } | |
1488 | } | |
1489 | 1489 | final Iterator<QuickDiffRangeDifference> iter= copy.iterator(); |
1490 | 1490 | return new Iterator<Annotation>() { |
1491 | 1491 |
+8
-8
24 | 24 | } |
25 | 25 | |
26 | 26 | private int hash(CharSequence seq){ |
27 | int hash = 5381; | |
28 | int len= seq.length(); | |
29 | for (int i= 0; i < len; i++) { | |
30 | char ch= seq.charAt(i); | |
31 | hash = ((hash << 5) + hash) + ch; /* hash * 33 + ch */ | |
32 | } | |
27 | int hash = 5381; | |
28 | int len= seq.length(); | |
29 | for (int i= 0; i < len; i++) { | |
30 | char ch= seq.charAt(i); | |
31 | hash = ((hash << 5) + hash) + ch; /* hash * 33 + ch */ | |
32 | } | |
33 | 33 | |
34 | return hash; | |
35 | } | |
34 | return hash; | |
35 | } | |
36 | 36 | |
37 | 37 | } |
+44
-26
0 | 0 | /******************************************************************************* |
1 | * Copyright (c) 2000, 2018 IBM Corporation and others. | |
1 | * Copyright (c) 2000, 2019 IBM Corporation and others. | |
2 | 2 | * |
3 | 3 | * This program and the accompanying materials |
4 | 4 | * are made available under the terms of the Eclipse Public License 2.0 |
1859 | 1859 | return false; |
1860 | 1860 | |
1861 | 1861 | return getInformationPresenter().openFocusedAnnotationHover(annotationHover, line); |
1862 | } | |
1862 | } | |
1863 | 1863 | |
1864 | 1864 | /** |
1865 | 1865 | * Returns the information presenter (creates it if necessary). |
2974 | 2974 | private long fMouseUpDelta= 0; |
2975 | 2975 | |
2976 | 2976 | private void triggerAction(String actionID, MouseEvent e) { |
2977 | IAction action= getAction(actionID); | |
2977 | // ActionId can be prefixed with modifiers | |
2978 | StringBuffer newActionId = new StringBuffer(""); //$NON-NLS-1$ | |
2979 | if ((e.stateMask & SWT.MOD1) > 0) { | |
2980 | newActionId.append("M1+"); //$NON-NLS-1$ | |
2981 | } | |
2982 | if ((e.stateMask & SWT.MOD2) > 0) { | |
2983 | newActionId.append("M2+"); //$NON-NLS-1$ | |
2984 | } | |
2985 | if ((e.stateMask & SWT.MOD3) > 0) { | |
2986 | newActionId.append("M3+"); //$NON-NLS-1$ | |
2987 | } | |
2988 | newActionId.append(actionID); | |
2989 | IAction action = getAction(newActionId.toString()); | |
2990 | // If action does not exist with specified | |
2991 | // modifiers+actionId, try to retrieve action with only | |
2992 | // actionId | |
2993 | if (action == null) { | |
2994 | action = getAction(actionID); | |
2995 | } | |
2978 | 2996 | if (action != null) { |
2979 | 2997 | if (action instanceof IUpdate) |
2980 | 2998 | ((IUpdate) action).update(); |
3155 | 3173 | } catch (InvocationTargetException x) { |
3156 | 3174 | Throwable t= x.getTargetException(); |
3157 | 3175 | if (t instanceof CoreException) { |
3158 | /* | |
3159 | /* XXX: Remove unpacking of CoreException once the following bug is | |
3160 | * fixed: https://bugs.eclipse.org/bugs/show_bug.cgi?id=81640 | |
3161 | */ | |
3162 | CoreException e= (CoreException)t; | |
3163 | IStatus status= e.getStatus(); | |
3164 | if (status.getException() != null) | |
3165 | throw new PartInitException(status); | |
3166 | throw new PartInitException(new Status(status.getSeverity(), status.getPlugin(), status.getCode(), status.getMessage(), t)); | |
3167 | } | |
3176 | /* | |
3177 | /* XXX: Remove unpacking of CoreException once the following bug is | |
3178 | * fixed: https://bugs.eclipse.org/bugs/show_bug.cgi?id=81640 | |
3179 | */ | |
3180 | CoreException e= (CoreException)t; | |
3181 | IStatus status= e.getStatus(); | |
3182 | if (status.getException() != null) | |
3183 | throw new PartInitException(status); | |
3184 | throw new PartInitException(new Status(status.getSeverity(), status.getPlugin(), status.getCode(), status.getMessage(), t)); | |
3185 | } | |
3168 | 3186 | throw new PartInitException(new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, EditorMessages.Editor_error_init, t)); |
3169 | 3187 | } |
3170 | 3188 | } |
3666 | 3684 | } |
3667 | 3685 | |
3668 | 3686 | /** |
3669 | * Tells whether the editor input should be included when adding object | |
3670 | * contributions to this editor's context menu. | |
3671 | * <p> | |
3672 | * This implementation always returns <code>true</code>. | |
3673 | * </p> | |
3674 | * | |
3687 | * Tells whether the editor input should be included when adding object | |
3688 | * contributions to this editor's context menu. | |
3689 | * <p> | |
3690 | * This implementation always returns <code>true</code>. | |
3691 | * </p> | |
3692 | * | |
3675 | 3693 | * @return <code>true</code> if the editor input should be considered |
3676 | * @since 3.2 | |
3694 | * @since 3.2 | |
3677 | 3695 | */ |
3678 | 3696 | protected boolean isEditorInputIncludedInContextMenu() { |
3679 | 3697 | return true; |
6031 | 6049 | * @param newGroup the new group |
6032 | 6050 | */ |
6033 | 6051 | protected final void addGroup(IMenuManager menu, String existingGroup, String newGroup) { |
6034 | IMenuManager subMenu= menu.findMenuUsingPath(existingGroup); | |
6035 | if (subMenu != null) | |
6036 | subMenu.add(new Separator(newGroup)); | |
6037 | else | |
6038 | menu.appendToGroup(existingGroup, new Separator(newGroup)); | |
6052 | IMenuManager subMenu= menu.findMenuUsingPath(existingGroup); | |
6053 | if (subMenu != null) | |
6054 | subMenu.add(new Separator(newGroup)); | |
6055 | else | |
6056 | menu.appendToGroup(existingGroup, new Separator(newGroup)); | |
6039 | 6057 | } |
6040 | 6058 | |
6041 | 6059 | /** |
6577 | 6595 | styledText.setCaret(fNonDefaultCaret); |
6578 | 6596 | fNonDefaultCaretImage= fNonDefaultCaret.getImage(); |
6579 | 6597 | } else if (fInitialCaret != styledText.getCaret()) |
6580 | styledText.setCaret(fInitialCaret); | |
6598 | styledText.setCaret(fInitialCaret); | |
6581 | 6599 | } |
6582 | 6600 | |
6583 | 6601 | private void disposeNonDefaultCaret() { |
+3
-3
846 | 846 | } |
847 | 847 | |
848 | 848 | /** |
849 | * Sets the preference key for go to previous navigation enablement. | |
850 | * | |
851 | * @param isGoToPreviousNavigationTargetKey the preference key | |
849 | * Sets the preference key for go to previous navigation enablement. | |
850 | * | |
851 | * @param isGoToPreviousNavigationTargetKey the preference key | |
852 | 852 | * @since 3.0 |
853 | 853 | */ |
854 | 854 | public void setIsGoToPreviousNavigationTargetKey(String isGoToPreviousNavigationTargetKey) { |
+5
-5
291 | 291 | * @since 3.2 |
292 | 292 | */ |
293 | 293 | private void addOrInsert(IContributionManager menu, IContributionItem item) { |
294 | String id= item.getId(); | |
294 | String id= item.getId(); | |
295 | 295 | if (menu.find(id) == null) |
296 | menu.add(item); | |
297 | else | |
298 | menu.insertAfter(id, item); | |
299 | } | |
296 | menu.add(item); | |
297 | else | |
298 | menu.insertAfter(id, item); | |
299 | } | |
300 | 300 | |
301 | 301 | @Override |
302 | 302 | public void contributeToStatusLine(IStatusLineManager statusLineManager) { |
+14
-14
131 | 131 | Set<Bundle> fBundleSet= new HashSet<>(length); |
132 | 132 | |
133 | 133 | for (int i= 0; i < length; i++) { |
134 | IConfigurationElement configElement= getConfigurationElement(elements[i]); | |
134 | IConfigurationElement configElement= getConfigurationElement(elements[i]); | |
135 | 135 | Bundle bundle= Platform.getBundle(configElement.getContributor().getName()); |
136 | 136 | fDescriptorMapping.put(elements[i], bundle.getSymbolicName()); |
137 | 137 | fBundleSet.add(bundle); |
147 | 147 | |
148 | 148 | String requires = bundle.getHeaders().get(Constants.REQUIRE_BUNDLE); |
149 | 149 | ManifestElement[] manifestElements; |
150 | try { | |
151 | manifestElements = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, requires); | |
152 | } catch (BundleException e) { | |
153 | String uid= getExtensionPointUniqueIdentifier(bundle); | |
154 | String message= "ConfigurationElementSorter for '" + uid + "': getting required plug-ins for '" + bundle.getSymbolicName() + "' failed"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
155 | Status status= new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, message, e); | |
156 | TextEditorPlugin.getDefault().getLog().log(status); | |
157 | continue; | |
158 | } | |
159 | ||
160 | if (manifestElements == null) | |
161 | continue; | |
150 | try { | |
151 | manifestElements = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, requires); | |
152 | } catch (BundleException e) { | |
153 | String uid= getExtensionPointUniqueIdentifier(bundle); | |
154 | String message= "ConfigurationElementSorter for '" + uid + "': getting required plug-ins for '" + bundle.getSymbolicName() + "' failed"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
155 | Status status= new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, message, e); | |
156 | TextEditorPlugin.getDefault().getLog().log(status); | |
157 | continue; | |
158 | } | |
159 | ||
160 | if (manifestElements == null) | |
161 | continue; | |
162 | 162 | |
163 | 163 | int i= 0; |
164 | 164 | while (i < manifestElements.length && !toTest.isEmpty()) { |
193 | 193 | while (iter.hasNext()) { |
194 | 194 | Entry<Object, String> entry= iter.next(); |
195 | 195 | if (bundleName.equals(entry.getValue())) { |
196 | IExtension extension = getConfigurationElement(entry.getKey()).getDeclaringExtension(); | |
196 | IExtension extension = getConfigurationElement(entry.getKey()).getDeclaringExtension(); | |
197 | 197 | return extension.getExtensionPointUniqueIdentifier(); |
198 | 198 | } |
199 | 199 | } |
+1
-1
289 | 289 | Assert.isLegal(target != null && shell != null); |
290 | 290 | fTarget= target; |
291 | 291 | fShell= shell; |
292 | update(); | |
292 | update(); | |
293 | 293 | } |
294 | 294 | |
295 | 295 | /** |
+40
-40
339 | 339 | */ |
340 | 340 | String TOGGLE_INSERT_MODE_ACTION= PREFIX + "toggle_insert_mode" + ACTION_POSTFIX; //$NON-NLS-1$;; |
341 | 341 | |
342 | /** | |
343 | * Help context id for the word completion action. | |
344 | * Value: <code>"org.eclipse.ui.hippie_completion_action_context"</code> | |
345 | * @since 3.1 | |
346 | */ | |
347 | String HIPPIE_COMPLETION_ACTION= PREFIX + "hippie_completion" + ACTION_POSTFIX; //$NON-NLS-1$ | |
348 | ||
349 | /** | |
350 | * Help context id for the content assist action. | |
351 | * Value: <code>"org.eclipse.ui.content_assist_action_context"</code> | |
352 | * @since 3.5 | |
353 | */ | |
354 | String CONTENT_ASSIST_ACTION= PREFIX + "content_assist" + ACTION_POSTFIX; //$NON-NLS-1$ | |
355 | ||
356 | /** | |
357 | * Help context id for the content assist context information action. | |
358 | * Value: <code>"org.eclipse.ui.content_assist_context_information_action_context"</code> | |
359 | * @since 3.5 | |
360 | */ | |
361 | String CONTENT_ASSIST_CONTEXT_INFORMATION_ACTION= PREFIX + "content_assist_context_information" + ACTION_POSTFIX; //$NON-NLS-1$ | |
362 | ||
363 | /** | |
364 | * Help context id for the quick assist action. | |
365 | * Value: <code>"org.eclipse.ui.quick_assist_action_context"</code> | |
366 | * @since 3.2 | |
367 | */ | |
368 | String QUICK_ASSIST_ACTION= PREFIX + "quick_assist" + ACTION_POSTFIX; //$NON-NLS-1$ | |
369 | ||
370 | /** | |
371 | * Help context id for the recenter action. | |
372 | * Value: <code>"org.eclipse.ui.recenter_action_context"</code> | |
373 | * @since 3.3 | |
374 | */ | |
375 | String RECENTER_ACTION= PREFIX + "recenter" + ACTION_POSTFIX; //$NON-NLS-1$ | |
376 | ||
377 | /** | |
378 | * Help context id for the show whitespace characters action. | |
379 | * Value: <code>"org.eclipse.ui.show_whitespace_characters_action_context"</code> | |
380 | * @since 3.3 | |
381 | */ | |
342 | /** | |
343 | * Help context id for the word completion action. | |
344 | * Value: <code>"org.eclipse.ui.hippie_completion_action_context"</code> | |
345 | * @since 3.1 | |
346 | */ | |
347 | String HIPPIE_COMPLETION_ACTION= PREFIX + "hippie_completion" + ACTION_POSTFIX; //$NON-NLS-1$ | |
348 | ||
349 | /** | |
350 | * Help context id for the content assist action. | |
351 | * Value: <code>"org.eclipse.ui.content_assist_action_context"</code> | |
352 | * @since 3.5 | |
353 | */ | |
354 | String CONTENT_ASSIST_ACTION= PREFIX + "content_assist" + ACTION_POSTFIX; //$NON-NLS-1$ | |
355 | ||
356 | /** | |
357 | * Help context id for the content assist context information action. | |
358 | * Value: <code>"org.eclipse.ui.content_assist_context_information_action_context"</code> | |
359 | * @since 3.5 | |
360 | */ | |
361 | String CONTENT_ASSIST_CONTEXT_INFORMATION_ACTION= PREFIX + "content_assist_context_information" + ACTION_POSTFIX; //$NON-NLS-1$ | |
362 | ||
363 | /** | |
364 | * Help context id for the quick assist action. | |
365 | * Value: <code>"org.eclipse.ui.quick_assist_action_context"</code> | |
366 | * @since 3.2 | |
367 | */ | |
368 | String QUICK_ASSIST_ACTION= PREFIX + "quick_assist" + ACTION_POSTFIX; //$NON-NLS-1$ | |
369 | ||
370 | /** | |
371 | * Help context id for the recenter action. | |
372 | * Value: <code>"org.eclipse.ui.recenter_action_context"</code> | |
373 | * @since 3.3 | |
374 | */ | |
375 | String RECENTER_ACTION= PREFIX + "recenter" + ACTION_POSTFIX; //$NON-NLS-1$ | |
376 | ||
377 | /** | |
378 | * Help context id for the show whitespace characters action. | |
379 | * Value: <code>"org.eclipse.ui.show_whitespace_characters_action_context"</code> | |
380 | * @since 3.3 | |
381 | */ | |
382 | 382 | String SHOW_WHITESPACE_CHARACTERS_ACTION= PREFIX + "show_whitepsace_characters" + ACTION_POSTFIX; //$NON-NLS-1$ |
383 | 383 | |
384 | 384 | /** |
148 | 148 | fText.setText(info); |
149 | 149 | } |
150 | 150 | |
151 | /** | |
151 | /** | |
152 | 152 | * Handles the property change. |
153 | 153 | * |
154 | 154 | * @param event the property change event object describing which property changed and how |
+4
-4
63 | 63 | * (described in <code>ResourceAction</code> constructor), or |
64 | 64 | * <code>null</code> if none |
65 | 65 | * @param style one of <code>IAction.AS_PUSH_BUTTON</code>, <code>IAction.AS_CHECK_BOX</code>, |
66 | * and <code>IAction.AS_RADIO_BUTTON</code>. | |
66 | * and <code>IAction.AS_RADIO_BUTTON</code>. | |
67 | 67 | * |
68 | 68 | * @see ResourceAction#ResourceAction(ResourceBundle, String, int) |
69 | 69 | * @see IAction#AS_CHECK_BOX |
108 | 108 | * (described in <code>ResourceAction</code> constructor), or <code>null</code> if none |
109 | 109 | * @param actionId the action id |
110 | 110 | * @param style one of <code>IAction.AS_PUSH_BUTTON</code>, <code>IAction.AS_CHECK_BOX</code>, |
111 | * and <code>IAction.AS_RADIO_BUTTON</code>. | |
111 | * and <code>IAction.AS_RADIO_BUTTON</code>. | |
112 | 112 | * |
113 | 113 | * @see ResourceAction#ResourceAction(ResourceBundle, String, int) |
114 | 114 | * @see IAction#AS_CHECK_BOX |
182 | 182 | |
183 | 183 | setEnabled(false); |
184 | 184 | if (getStyle() == AS_CHECK_BOX || getStyle() == AS_RADIO_BUTTON) |
185 | setChecked(false); | |
185 | setChecked(false); | |
186 | 186 | setText(fDefaultText); |
187 | 187 | setToolTipText(""); //$NON-NLS-1$ |
188 | 188 | |
190 | 190 | |
191 | 191 | setEnabled(fAction.isEnabled()); |
192 | 192 | if (fAction.getStyle() == AS_CHECK_BOX || fAction.getStyle() == AS_RADIO_BUTTON) |
193 | super.setChecked(fAction.isChecked()); | |
193 | super.setChecked(fAction.isChecked()); | |
194 | 194 | setText(fAction.getText()); |
195 | 195 | setToolTipText(fAction.getToolTipText()); |
196 | 196 | fAction.addPropertyChangeListener(fListener); |
+1
-1
506 | 506 | |
507 | 507 | /* |
508 | 508 | * @see AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent) |
509 | */ | |
509 | */ | |
510 | 510 | protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { |
511 | 511 | |
512 | 512 | String p= event.getProperty(); |
+31
-31
187 | 187 | |
188 | 188 | @Override |
189 | 189 | protected void layout(Composite composite, boolean flushCache) { |
190 | Rectangle area= composite.getClientArea(); | |
191 | Table table= getTable(composite); | |
192 | int tableWidth= table.getSize().x; | |
193 | int trim= computeTrim(area, table, tableWidth); | |
194 | int width= Math.max(0, area.width - trim); | |
195 | ||
196 | if (width > 1) | |
197 | layoutTable(table, width, area, tableWidth < area.width); | |
198 | ||
199 | if( composite.getData(RECALCULATE_LAYOUT) == null ) { | |
200 | composite.setData(RECALCULATE_LAYOUT, Boolean.FALSE); | |
201 | composite.layout(); | |
202 | } | |
190 | Rectangle area= composite.getClientArea(); | |
191 | Table table= getTable(composite); | |
192 | int tableWidth= table.getSize().x; | |
193 | int trim= computeTrim(area, table, tableWidth); | |
194 | int width= Math.max(0, area.width - trim); | |
195 | ||
196 | if (width > 1) | |
197 | layoutTable(table, width, area, tableWidth < area.width); | |
198 | ||
199 | if( composite.getData(RECALCULATE_LAYOUT) == null ) { | |
200 | composite.setData(RECALCULATE_LAYOUT, Boolean.FALSE); | |
201 | composite.layout(); | |
202 | } | |
203 | 203 | } |
204 | 204 | |
205 | 205 | private int computeTrim(Rectangle area, Table table, int tableWidth) { |
206 | 206 | Point preferredSize= computeTableSize(table, area.width, area.height); |
207 | int trim; | |
208 | if (tableWidth > 1) { | |
209 | trim= tableWidth - table.getClientArea().width; | |
210 | } else { | |
211 | // initially, the table has no extend and no client area - use the border with | |
212 | // plus some padding as educated guess | |
213 | trim= 2 * table.getBorderWidth() + 1 ; | |
214 | } | |
215 | if (preferredSize.y > area.height) { | |
216 | // Subtract the scrollbar width from the total column width | |
217 | // if a vertical scrollbar will be required, but is not currently showing | |
218 | // (in which case it is already subtracted above) | |
219 | ScrollBar vBar= table.getVerticalBar(); | |
220 | if (!vBar.isVisible()) { | |
221 | Point vBarSize= vBar.getSize(); | |
222 | trim += vBarSize.x; | |
223 | } | |
224 | } | |
207 | int trim; | |
208 | if (tableWidth > 1) { | |
209 | trim= tableWidth - table.getClientArea().width; | |
210 | } else { | |
211 | // initially, the table has no extend and no client area - use the border with | |
212 | // plus some padding as educated guess | |
213 | trim= 2 * table.getBorderWidth() + 1 ; | |
214 | } | |
215 | if (preferredSize.y > area.height) { | |
216 | // Subtract the scrollbar width from the total column width | |
217 | // if a vertical scrollbar will be required, but is not currently showing | |
218 | // (in which case it is already subtracted above) | |
219 | ScrollBar vBar= table.getVerticalBar(); | |
220 | if (!vBar.isVisible()) { | |
221 | Point vBarSize= vBar.getSize(); | |
222 | trim += vBarSize.x; | |
223 | } | |
224 | } | |
225 | 225 | return trim; |
226 | 226 | } |
227 | 227 |
+22
-22
831 | 831 | layout.marginWidth= 0; |
832 | 832 | parent.setLayout(layout); |
833 | 833 | |
834 | Composite innerParent= new Composite(parent, SWT.NONE); | |
835 | GridLayout innerLayout= new GridLayout(); | |
836 | innerLayout.numColumns= 2; | |
837 | innerLayout.marginHeight= 0; | |
838 | innerLayout.marginWidth= 0; | |
839 | innerParent.setLayout(innerLayout); | |
840 | GridData gd= new GridData(GridData.FILL_BOTH); | |
841 | gd.horizontalSpan= 2; | |
842 | innerParent.setLayoutData(gd); | |
843 | ||
844 | Composite tableComposite= new Composite(innerParent, SWT.NONE); | |
845 | GridData data= new GridData(GridData.FILL_BOTH); | |
846 | data.widthHint= 360; | |
847 | data.heightHint= convertHeightInCharsToPixels(10); | |
848 | tableComposite.setLayoutData(data); | |
849 | ||
850 | ColumnLayout columnLayout= new ColumnLayout(); | |
851 | tableComposite.setLayout(columnLayout); | |
834 | Composite innerParent= new Composite(parent, SWT.NONE); | |
835 | GridLayout innerLayout= new GridLayout(); | |
836 | innerLayout.numColumns= 2; | |
837 | innerLayout.marginHeight= 0; | |
838 | innerLayout.marginWidth= 0; | |
839 | innerParent.setLayout(innerLayout); | |
840 | GridData gd= new GridData(GridData.FILL_BOTH); | |
841 | gd.horizontalSpan= 2; | |
842 | innerParent.setLayoutData(gd); | |
843 | ||
844 | Composite tableComposite= new Composite(innerParent, SWT.NONE); | |
845 | GridData data= new GridData(GridData.FILL_BOTH); | |
846 | data.widthHint= 360; | |
847 | data.heightHint= convertHeightInCharsToPixels(10); | |
848 | tableComposite.setLayoutData(data); | |
849 | ||
850 | ColumnLayout columnLayout= new ColumnLayout(); | |
851 | tableComposite.setLayout(columnLayout); | |
852 | 852 | Table table= new Table(tableComposite, SWT.CHECK | SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL); |
853 | 853 | |
854 | 854 | table.setHeaderVisible(true); |
958 | 958 | if (isShowFormatterSetting()) { |
959 | 959 | fFormatButton= new Button(parent, SWT.CHECK); |
960 | 960 | fFormatButton.setText(TemplatesMessages.TemplatePreferencePage_use_code_formatter); |
961 | GridData gd1= new GridData(); | |
962 | gd1.horizontalSpan= 2; | |
963 | fFormatButton.setLayoutData(gd1); | |
964 | fFormatButton.setSelection(getPreferenceStore().getBoolean(getFormatterPreferenceKey())); | |
961 | GridData gd1= new GridData(); | |
962 | gd1.horizontalSpan= 2; | |
963 | fFormatButton.setLayoutData(gd1); | |
964 | fFormatButton.setSelection(getPreferenceStore().getBoolean(getFormatterPreferenceKey())); | |
965 | 965 | } |
966 | 966 | |
967 | 967 | fTableViewer.setInput(fTemplateStore); |
+3
-3
68 | 68 | |
69 | 69 | @Override |
70 | 70 | protected Object nativeToJava(TransferData transferData) { |
71 | Object result= super.nativeToJava(transferData); | |
72 | if (!(result instanceof byte[]) || !TYPE_NAME.equals(new String((byte[]) result))) | |
73 | return null ; | |
71 | Object result= super.nativeToJava(transferData); | |
72 | if (!(result instanceof byte[]) || !TYPE_NAME.equals(new String((byte[]) result))) | |
73 | return null ; | |
74 | 74 | return fObject ; |
75 | 75 | } |
76 | 76 | } |
1 | 1 | Bundle-ManifestVersion: 2 |
2 | 2 | Bundle-Name: %Plugin.name |
3 | 3 | Bundle-SymbolicName: org.eclipse.ui.workbench.texteditor.tests |
4 | Bundle-Version: 3.12.200.qualifier | |
4 | Bundle-Version: 3.12.300.qualifier | |
5 | 5 | Bundle-Vendor: %Plugin.providerName |
6 | 6 | Bundle-Localization: plugin |
7 | 7 | Export-Package: |
13 | 13 | <parent> |
14 | 14 | <artifactId>tests-pom</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | <relativePath>../tests-pom/</relativePath> |
18 | 18 | </parent> |
19 | 19 | <groupId>org.eclipse.ui</groupId> |
20 | 20 | <artifactId>org.eclipse.ui.workbench.texteditor.tests</artifactId> |
21 | <version>3.12.200-SNAPSHOT</version> | |
21 | <version>3.12.300-SNAPSHOT</version> | |
22 | 22 | <packaging>eclipse-test-plugin</packaging> |
23 | 23 | <properties> |
24 | 24 | <testSuite>${project.artifactId}</testSuite> |
+17
-17
160 | 160 | list= fEngine.getCompletionsBackwards(documents[0], |
161 | 161 | "pri", documents[0].get().indexOf("println") + 1); |
162 | 162 | assertEquals(list.size(), 1); |
163 | assertEquals(list.get(0), "nt"); | |
163 | assertEquals(list.get(0), "nt"); | |
164 | 164 | |
165 | 165 | list= fEngine.getCompletionsBackwards(documents[0], |
166 | 166 | "pa", 2); |
191 | 191 | } |
192 | 192 | |
193 | 193 | @Test |
194 | public void testSearchBackwards3() { | |
195 | try { | |
196 | List<String> list= fEngine.getCompletionsBackwards(documents[1], | |
197 | "test", documents[1].getLength()); | |
198 | assertEquals("Number of backwards suggestions does not match", 2, list.size()); | |
199 | list= fEngine.getCompletionsBackwards(documents[1], | |
200 | "tests", documents[1].getLength()); | |
201 | assertEquals("Number of backwards suggestions does not match", 1, list.size()); | |
202 | ||
203 | list= fEngine.getCompletionsBackwards(documents[1], | |
204 | "test", documents[1].getLength() - 1); | |
205 | assertEquals("Number of backwards suggestions does not match", 1, list.size()); | |
206 | } catch (BadLocationException e) { | |
207 | assertTrue("Got out of document bounds", false); | |
208 | } | |
209 | } | |
194 | public void testSearchBackwards3() { | |
195 | try { | |
196 | List<String> list= fEngine.getCompletionsBackwards(documents[1], | |
197 | "test", documents[1].getLength()); | |
198 | assertEquals("Number of backwards suggestions does not match", 2, list.size()); | |
199 | list= fEngine.getCompletionsBackwards(documents[1], | |
200 | "tests", documents[1].getLength()); | |
201 | assertEquals("Number of backwards suggestions does not match", 1, list.size()); | |
202 | ||
203 | list= fEngine.getCompletionsBackwards(documents[1], | |
204 | "test", documents[1].getLength() - 1); | |
205 | assertEquals("Number of backwards suggestions does not match", 1, list.size()); | |
206 | } catch (BadLocationException e) { | |
207 | assertTrue("Got out of document bounds", false); | |
208 | } | |
209 | } | |
210 | 210 | |
211 | 211 | @Test |
212 | 212 | public void testSearch() { |
+38
-38
53 | 53 | |
54 | 54 | Display display= Display.getDefault(); |
55 | 55 | |
56 | Event event= new Event(); | |
57 | event.type= SWT.KeyDown; | |
58 | event.keyCode= SWT.CTRL; | |
59 | System.out.println("* CTRL " + display.post(event)); | |
60 | event.keyCode= SWT.SHIFT; | |
61 | System.out.println("* SHIFT " + display.post(event)); | |
62 | event.character= SWT.ESC; | |
63 | event.keyCode= SWT.ESC; | |
64 | System.out.println("* ESC " + display.post(event)); | |
65 | ||
66 | event.type= SWT.KeyUp; | |
67 | System.out.println("* ESC up " + display.post(event)); | |
68 | event.character= 0; | |
69 | event.keyCode= SWT.SHIFT; | |
70 | System.out.println("* SHIFT up " + display.post(event)); | |
71 | event.keyCode= SWT.CTRL; | |
72 | System.out.println("* CTRL up " + display.post(event)); | |
73 | ||
74 | runEventQueue(); | |
75 | takeScreenshot(ScreenshotTest.class, testName.getMethodName() + 2, System.out); | |
76 | ||
77 | event.type= SWT.KeyDown; | |
78 | event.character= SWT.ESC; | |
79 | event.keyCode= SWT.ESC; | |
80 | System.out.println("* ESC " + display.post(event)); | |
81 | event.type= SWT.KeyUp; | |
82 | System.out.println("* ESC up " + display.post(event)); | |
83 | ||
84 | runEventQueue(); | |
85 | takeScreenshot(ScreenshotTest.class, testName.getMethodName() + 3, System.out); | |
56 | Event event= new Event(); | |
57 | event.type= SWT.KeyDown; | |
58 | event.keyCode= SWT.CTRL; | |
59 | System.out.println("* CTRL " + display.post(event)); | |
60 | event.keyCode= SWT.SHIFT; | |
61 | System.out.println("* SHIFT " + display.post(event)); | |
62 | event.character= SWT.ESC; | |
63 | event.keyCode= SWT.ESC; | |
64 | System.out.println("* ESC " + display.post(event)); | |
65 | ||
66 | event.type= SWT.KeyUp; | |
67 | System.out.println("* ESC up " + display.post(event)); | |
68 | event.character= 0; | |
69 | event.keyCode= SWT.SHIFT; | |
70 | System.out.println("* SHIFT up " + display.post(event)); | |
71 | event.keyCode= SWT.CTRL; | |
72 | System.out.println("* CTRL up " + display.post(event)); | |
73 | ||
74 | runEventQueue(); | |
75 | takeScreenshot(ScreenshotTest.class, testName.getMethodName() + 2, System.out); | |
76 | ||
77 | event.type= SWT.KeyDown; | |
78 | event.character= SWT.ESC; | |
79 | event.keyCode= SWT.ESC; | |
80 | System.out.println("* ESC " + display.post(event)); | |
81 | event.type= SWT.KeyUp; | |
82 | System.out.println("* ESC up " + display.post(event)); | |
83 | ||
84 | runEventQueue(); | |
85 | takeScreenshot(ScreenshotTest.class, testName.getMethodName() + 3, System.out); | |
86 | 86 | } |
87 | 87 | |
88 | 88 | /** |
89 | 89 | * Takes a screenshot and dumps other debugging information to the given stream. |
90 | 90 | * |
91 | * <p> | |
92 | * Workaround for missing {@link junit.framework.TestCase#getName()} in JUnit 4: | |
93 | * </p> | |
94 | * | |
95 | * <pre> | |
96 | * @Rule | |
97 | * public TestName testName = new TestName(); | |
98 | * </pre> | |
91 | * <p> | |
92 | * Workaround for missing {@link junit.framework.TestCase#getName()} in JUnit 4: | |
93 | * </p> | |
94 | * | |
95 | * <pre> | |
96 | * @Rule | |
97 | * public TestName testName = new TestName(); | |
98 | * </pre> | |
99 | 99 | * |
100 | 100 | * @param testClass test class that takes the screenshot |
101 | 101 | * @param name screenshot identifier (e.g. test name) |
14 | 14 | <parent> |
15 | 15 | <groupId>org.eclipse</groupId> |
16 | 16 | <artifactId>eclipse-platform-parent</artifactId> |
17 | <version>4.11.0-SNAPSHOT</version> | |
17 | <version>4.12.0-SNAPSHOT</version> | |
18 | 18 | <relativePath>../eclipse-platform-parent</relativePath> |
19 | 19 | </parent> |
20 | 20 | |
21 | 21 | <groupId>eclipse.platform.text</groupId> |
22 | 22 | <artifactId>eclipse.platform.text</artifactId> |
23 | <version>4.11.0-SNAPSHOT</version> | |
23 | <version>4.12.0-SNAPSHOT</version> | |
24 | 24 | <packaging>pom</packaging> |
25 | 25 | |
26 | 26 | <properties> |
13 | 13 | <parent> |
14 | 14 | <artifactId>eclipse.platform.text</artifactId> |
15 | 15 | <groupId>eclipse.platform.text</groupId> |
16 | <version>4.11.0-SNAPSHOT</version> | |
16 | <version>4.12.0-SNAPSHOT</version> | |
17 | 17 | </parent> |
18 | 18 | <artifactId>tests-pom</artifactId> |
19 | <version>4.11.0-SNAPSHOT</version> | |
19 | <version>4.12.0-SNAPSHOT</version> | |
20 | 20 | <packaging>pom</packaging> |
21 | 21 | <properties> |
22 | 22 | <code.ignoredWarnings>${tests.ignoredWarnings}</code.ignoredWarnings> |