1 /** 2 * Copyright (c) 2017 Angelo ZERR. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Angelo Zerr <angelo.zerr@gmail.com> - [CodeMining] Provide inline annotations support - Bug 527675 13 */ 14 package org.eclipse.jface.text.examples.sources.inlined; 15 16 import java.util.HashSet; 17 import java.util.Set; 18 import java.util.regex.Matcher; 19 import java.util.regex.Pattern; 20 21 import org.eclipse.jface.text.BadLocationException; 22 import org.eclipse.jface.text.Document; 23 import org.eclipse.jface.text.IDocument; 24 import org.eclipse.jface.text.IRegion; 25 import org.eclipse.jface.text.ITextViewerExtension2; 26 import org.eclipse.jface.text.Position; 27 import org.eclipse.jface.text.reconciler.DirtyRegion; 28 import org.eclipse.jface.text.reconciler.IReconcilingStrategy; 29 import org.eclipse.jface.text.reconciler.MonoReconciler; 30 import org.eclipse.jface.text.source.Annotation; 31 import org.eclipse.jface.text.source.AnnotationModel; 32 import org.eclipse.jface.text.source.AnnotationPainter; 33 import org.eclipse.jface.text.source.IAnnotationAccess; 34 import org.eclipse.jface.text.source.ISourceViewer; 35 import org.eclipse.jface.text.source.SourceViewer; 36 import org.eclipse.jface.text.source.inlined.AbstractInlinedAnnotation; 37 import org.eclipse.jface.text.source.inlined.InlinedAnnotationSupport; 38 import org.eclipse.jface.text.source.inlined.LineContentAnnotation; 39 import org.eclipse.jface.text.source.inlined.LineHeaderAnnotation; 40 import org.eclipse.jface.text.source.inlined.Positions; 41 import org.eclipse.swt.SWT; 42 import org.eclipse.swt.graphics.Color; 43 import org.eclipse.swt.graphics.Device; 44 import org.eclipse.swt.layout.FillLayout; 45 import org.eclipse.swt.widgets.Display; 46 import org.eclipse.swt.widgets.Shell; 47 48 /** 49 * An inlined demo with {@link LineHeaderAnnotation} and 50 * {@link LineContentAnnotation} annotations both: 51 * 52 * <ul> 53 * <li>a status OK, NOK is displayed before the line which starts with 'color:'. 54 * This status is the result of the content after 'color' which must be a rgb 55 * content. Here {@link ColorStatusAnnotation} is used.</li> 56 * <li>a colorized square is displayed before the rgb declaration (inside the 57 * line content). Here {@link ColorAnnotation} is used.</li> 58 * </ul> 59 * 60 */ 61 public class InlinedAnnotationDemo { 62 main(String[] args)63 public static void main(String[] args) throws Exception { 64 65 Display display = new Display(); 66 Shell shell = new Shell(display); 67 shell.setLayout(new FillLayout()); 68 shell.setText("Inlined annotation demo"); 69 70 // Create source viewer and initialize the content 71 ISourceViewer sourceViewer = new SourceViewer(shell, null, SWT.V_SCROLL | SWT.BORDER); 72 sourceViewer.setDocument(new Document("\ncolor:rgb(255, 255, 0)"), new AnnotationModel()); 73 74 // Initialize inlined annotations support 75 InlinedAnnotationSupport support = new InlinedAnnotationSupport(); 76 support.install(sourceViewer, createAnnotationPainter(sourceViewer)); 77 78 // Refresh inlined annotation in none UI Thread with reconciler. 79 MonoReconciler reconciler = new MonoReconciler(new IReconcilingStrategy() { 80 81 @Override 82 public void setDocument(IDocument document) { 83 Set<AbstractInlinedAnnotation> annotations = getInlinedAnnotation(sourceViewer, support); 84 support.updateAnnotations(annotations); 85 } 86 87 @Override 88 public void reconcile(IRegion partition) { 89 Set<AbstractInlinedAnnotation> anns = getInlinedAnnotation(sourceViewer, support); 90 support.updateAnnotations(anns); 91 } 92 93 @Override 94 public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { 95 96 } 97 }, false); 98 reconciler.setDelay(1); 99 reconciler.install(sourceViewer); 100 101 shell.open(); 102 while (!shell.isDisposed()) { 103 if (!display.readAndDispatch()) 104 display.sleep(); 105 } 106 display.dispose(); 107 } 108 109 /** 110 * Create annotation painter. 111 * 112 * @param viewer 113 * the viewer. 114 * @return annotation painter. 115 */ createAnnotationPainter(ISourceViewer viewer)116 private static AnnotationPainter createAnnotationPainter(ISourceViewer viewer) { 117 IAnnotationAccess annotationAccess = new IAnnotationAccess() { 118 @Override 119 public Object getType(Annotation annotation) { 120 return annotation.getType(); 121 } 122 123 @Override 124 public boolean isMultiLine(Annotation annotation) { 125 return true; 126 } 127 128 @Override 129 public boolean isTemporary(Annotation annotation) { 130 return true; 131 } 132 133 }; 134 AnnotationPainter painter = new AnnotationPainter(viewer, annotationAccess); 135 ((ITextViewerExtension2) viewer).addPainter(painter); 136 return painter; 137 } 138 139 /** 140 * Returns the inlined annotations list to display in the given viewer. 141 * 142 * @param viewer 143 * the viewer 144 * @param support 145 * the inlined annotation suppor. 146 * @return the inlined annotations list to display in the given viewer. 147 */ getInlinedAnnotation(ISourceViewer viewer, InlinedAnnotationSupport support)148 private static Set<AbstractInlinedAnnotation> getInlinedAnnotation(ISourceViewer viewer, 149 InlinedAnnotationSupport support) { 150 IDocument document = viewer.getDocument(); 151 Set<AbstractInlinedAnnotation> annotations = new HashSet<>(); 152 int lineCount = document.getNumberOfLines(); 153 for (int i = 0; i < lineCount; i++) { 154 String line = getLineText(document, i).trim(); 155 int index = line.indexOf("color:"); 156 if (index == 0) { 157 String rgb = line.substring(index + "color:".length(), line.length()).trim(); 158 try { 159 String status = "OK!"; 160 Color color = parse(rgb, viewer.getTextWidget().getDisplay()); 161 if (color != null) { 162 } else { 163 status = "ERROR!"; 164 } 165 // Status color annotation 166 Position pos = Positions.of(i, document, true); 167 ColorStatusAnnotation statusAnnotation = support.findExistingAnnotation(pos); 168 if (statusAnnotation == null) { 169 statusAnnotation = new ColorStatusAnnotation(pos, viewer); 170 } 171 statusAnnotation.setStatus(status); 172 annotations.add(statusAnnotation); 173 174 // Color annotation 175 if (color != null) { 176 Position colorPos = new Position(pos.offset + index + "color:".length(), 1); 177 ColorAnnotation colorAnnotation = support.findExistingAnnotation(colorPos); 178 if (colorAnnotation == null) { 179 colorAnnotation = new ColorAnnotation(colorPos, viewer); 180 } 181 colorAnnotation.setColor(color); 182 annotations.add(colorAnnotation); 183 } 184 185 // rgb parameter names annotations 186 int rgbIndex = line.indexOf("rgb"); 187 if (rgbIndex != -1) { 188 rgbIndex = rgbIndex + "rgb".length(); 189 int startOffset = pos.offset + rgbIndex; 190 String rgbContent = line.substring(rgbIndex, line.length()); 191 int startIndex = addRGBParamNameAnnotation("red:", rgbContent, 0, startOffset, viewer, support, 192 annotations); 193 if (startIndex != -1) { 194 startIndex = addRGBParamNameAnnotation("green:", rgbContent, startIndex, startOffset, viewer, 195 support, annotations); 196 if (startIndex != -1) { 197 startIndex = addRGBParamNameAnnotation("blue:", rgbContent, startIndex, startOffset, 198 viewer, support, annotations); 199 } 200 } 201 } 202 203 } catch (BadLocationException e) { 204 e.printStackTrace(); 205 } 206 } 207 } 208 return annotations; 209 } 210 211 /** 212 * Add RGB parameter name annotation 213 * 214 * @param paramName 215 * @param rgbContent 216 * @param startIndex 217 * @param startOffset 218 * @param viewer 219 * @param support 220 * @param annotations 221 * @return the current parsed index 222 */ addRGBParamNameAnnotation(String paramName, String rgbContent, int startIndex, int startOffset, ISourceViewer viewer, InlinedAnnotationSupport support, Set<AbstractInlinedAnnotation> annotations)223 private static int addRGBParamNameAnnotation(String paramName, String rgbContent, int startIndex, int startOffset, 224 ISourceViewer viewer, InlinedAnnotationSupport support, Set<AbstractInlinedAnnotation> annotations) { 225 char startChar = startIndex == 0 ? '(' : ','; 226 char[] chars = rgbContent.toCharArray(); 227 for (int i = startIndex; i < chars.length; i++) { 228 char c = chars[i]; 229 if (c == startChar) { 230 if (i == chars.length - 1) { 231 return -1; 232 } 233 Position paramPos = new Position(startOffset + i + 1, 1); 234 LineContentAnnotation colorParamAnnotation = support.findExistingAnnotation(paramPos); 235 if (colorParamAnnotation == null) { 236 colorParamAnnotation = new LineContentAnnotation(paramPos, viewer); 237 } 238 colorParamAnnotation.setText(paramName); 239 annotations.add(colorParamAnnotation); 240 return i + 1; 241 } 242 } 243 return -1; 244 } 245 246 /** 247 * Parse the given input rgb color and returns an instance of SWT Color and null 248 * otherwise. 249 * 250 * @param input 251 * the rgb string color 252 * @param device 253 * @return the created color and null otherwise. 254 */ parse(String input, Device device)255 private static Color parse(String input, Device device) { 256 Pattern c = Pattern.compile("rgb *\\( *([0-9]+), *([0-9]+), *([0-9]+) *\\)"); 257 Matcher m = c.matcher(input); 258 if (m.matches()) { 259 try { 260 return new Color(device, Integer.valueOf(m.group(1)), // r 261 Integer.valueOf(m.group(2)), // g 262 Integer.valueOf(m.group(3))); // b 263 } catch (Exception e) { 264 265 } 266 } 267 return null; 268 } 269 270 /** 271 * Returns the line text. 272 * 273 * @param document 274 * the document. 275 * @param line 276 * the line index. 277 * @return the line text. 278 */ getLineText(IDocument document, int line)279 private static String getLineText(IDocument document, int line) { 280 try { 281 int offset = document.getLineOffset(line); 282 int length = document.getLineLength(line); 283 return document.get(offset, length); 284 } catch (Exception e) { 285 e.printStackTrace(); 286 return null; 287 } 288 } 289 } 290