redcar-javamateview 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/LICENSE +34 -0
  2. data/README +58 -0
  3. data/Rakefile +94 -0
  4. data/lib/javamateview.rb +41 -0
  5. data/lib/javamateview/example.rb +334 -0
  6. data/lib/javamateview/jar/java-mateview.jar +0 -0
  7. data/lib/javamateview/jcodings.jar +0 -0
  8. data/lib/javamateview/jdom.jar +0 -0
  9. data/lib/javamateview/joni.jar +0 -0
  10. data/spec/onig/match_spec.rb +50 -0
  11. data/spec/parsing/dynamic_parsing_spec.rb +172 -0
  12. data/spec/parsing/static_parsing_spec.rb +476 -0
  13. data/spec/spec_helper.rb +33 -0
  14. data/src/com/redcareditor/mate/Bundle.java +81 -0
  15. data/src/com/redcareditor/mate/DoublePattern.java +89 -0
  16. data/src/com/redcareditor/mate/Grammar.java +129 -0
  17. data/src/com/redcareditor/mate/IAnnotationAreaListener.java +7 -0
  18. data/src/com/redcareditor/mate/IGrammarListener.java +5 -0
  19. data/src/com/redcareditor/mate/IncludePattern.java +10 -0
  20. data/src/com/redcareditor/mate/LineNumberRulerColumn.java +922 -0
  21. data/src/com/redcareditor/mate/Marker.java +22 -0
  22. data/src/com/redcareditor/mate/MateText.java +697 -0
  23. data/src/com/redcareditor/mate/ParseThunk.java +71 -0
  24. data/src/com/redcareditor/mate/Parser.java +627 -0
  25. data/src/com/redcareditor/mate/ParserScheduler.java +237 -0
  26. data/src/com/redcareditor/mate/Pattern.java +152 -0
  27. data/src/com/redcareditor/mate/RangeSet.java +91 -0
  28. data/src/com/redcareditor/mate/Scanner.java +178 -0
  29. data/src/com/redcareditor/mate/Scope.java +534 -0
  30. data/src/com/redcareditor/mate/ScopeMatcher.java +162 -0
  31. data/src/com/redcareditor/mate/SharedTextColors.java +110 -0
  32. data/src/com/redcareditor/mate/SinglePattern.java +20 -0
  33. data/src/com/redcareditor/mate/WhitespaceCharacterPainter.java +395 -0
  34. data/src/com/redcareditor/mate/colouring/Colourer.java +16 -0
  35. data/src/com/redcareditor/mate/colouring/swt/MarginPaintListener.java +62 -0
  36. data/src/com/redcareditor/mate/colouring/swt/SwtColourer.java +501 -0
  37. data/src/com/redcareditor/mate/document/MateDocument.java +15 -0
  38. data/src/com/redcareditor/mate/document/MateTextFactory.java +9 -0
  39. data/src/com/redcareditor/mate/document/MateTextLocation.java +8 -0
  40. data/src/com/redcareditor/mate/document/MateTextLocationComparator.java +17 -0
  41. data/src/com/redcareditor/mate/document/MateTextRange.java +18 -0
  42. data/src/com/redcareditor/mate/document/swt/SwtMateDocument.java +143 -0
  43. data/src/com/redcareditor/mate/document/swt/SwtMateTextLocation.java +88 -0
  44. data/src/com/redcareditor/mate/document/swt/SwtMateTextRange.java +92 -0
  45. data/src/com/redcareditor/mate/document/swt/SwtScopePositionUpdater.java +90 -0
  46. data/src/com/redcareditor/mate/undo/MateTextUndoManager.java +11 -0
  47. data/src/com/redcareditor/mate/undo/swt/SwtMateTextUndoManager.java +166 -0
  48. data/src/com/redcareditor/onig/Match.java +212 -0
  49. data/src/com/redcareditor/onig/NullMatch.java +57 -0
  50. data/src/com/redcareditor/onig/NullRx.java +29 -0
  51. data/src/com/redcareditor/onig/Range.java +45 -0
  52. data/src/com/redcareditor/onig/Rx.java +167 -0
  53. data/src/com/redcareditor/plist/Dict.java +119 -0
  54. data/src/com/redcareditor/plist/PlistNode.java +52 -0
  55. data/src/com/redcareditor/plist/PlistPropertyLoader.java +44 -0
  56. data/src/com/redcareditor/theme/ScopeSelector.java +39 -0
  57. data/src/com/redcareditor/theme/Theme.java +122 -0
  58. data/src/com/redcareditor/theme/ThemeManager.java +41 -0
  59. data/src/com/redcareditor/theme/ThemeSetting.java +78 -0
  60. data/src/com/redcareditor/util/FileUtility.java +64 -0
  61. data/src/com/redcareditor/util/SingleLineFormatter.java +11 -0
  62. data/src/com/redcareditor/util/swt/ColourUtil.java +56 -0
  63. data/src/ruby/java-mateview.rb +68 -0
  64. data/test/com/redcareditor/mate/BundleTest.java +33 -0
  65. data/test/com/redcareditor/mate/EmptyRangeSetTest.java +27 -0
  66. data/test/com/redcareditor/mate/FilledRangeSetTest.java +82 -0
  67. data/test/com/redcareditor/mate/GrammarTest.java +158 -0
  68. data/test/com/redcareditor/mate/MateTextTest.java +35 -0
  69. data/test/com/redcareditor/mate/ScopeMatcherMatchingTest.java +55 -0
  70. data/test/com/redcareditor/mate/ScopeMatcherRankingTest.java +40 -0
  71. data/test/com/redcareditor/onig/RxTest.java +54 -0
  72. data/test/com/redcareditor/plist/DictTest.java +33 -0
  73. data/test/com/redcareditor/theme/RailsCastThemeTest.java +37 -0
  74. data/test/com/redcareditor/theme/ScopeSelectorTest.java +38 -0
  75. data/test/com/redcareditor/theme/ThemeManagerTest.java +29 -0
  76. data/test/com/redcareditor/util/swt/ColourUtilTest.java +17 -0
  77. metadata +134 -0
@@ -0,0 +1,162 @@
1
+ package com.redcareditor.mate;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.List;
5
+
6
+ import com.redcareditor.onig.Match;
7
+ import com.redcareditor.onig.Range;
8
+ import com.redcareditor.onig.Rx;
9
+
10
+ public class ScopeMatcher {
11
+ public Rx pos_rx;
12
+ public List<Rx> neg_rxs;
13
+
14
+ public static List<Integer> occurrences(String target, String find) {
15
+ List<Integer> positions = new ArrayList<Integer>();
16
+ int fromIndex = 0;
17
+ int newIndex = -1;
18
+ while ((newIndex = target.indexOf(find, fromIndex)) != -1) {
19
+ positions.add(newIndex);
20
+ fromIndex = newIndex + 1;
21
+ }
22
+ return positions;
23
+ }
24
+
25
+ // returns 1 if m1 is better than m2, -1 if m1 is worse than m2, 0 if
26
+ // equally good
27
+ public static int compareMatch(String scopeString, Match m1, Match m2) {
28
+ List<Integer> spaceIxs = occurrences(scopeString, " ");
29
+ int max_cap1 = m1.numCaptures();
30
+ int max_cap2 = m2.numCaptures();
31
+ int cap1_ix, cap1_el_ix, len1;
32
+ int cap2_ix, cap2_el_ix, len2;
33
+ for (int i = 0; i < Math.min(max_cap1, max_cap2); i++) {
34
+ // first try element depth:
35
+ Range capture1 = m1.getCapture(max_cap1 - 1 - i);
36
+ Range capture2 = m2.getCapture(max_cap2 - 1 - i);
37
+
38
+ cap1_ix = capture1.start;
39
+ cap2_ix = capture2.start;
40
+ cap1_el_ix = ScopeMatcher.sorted_ix(spaceIxs, cap1_ix);
41
+ cap2_el_ix = ScopeMatcher.sorted_ix(spaceIxs, cap2_ix);
42
+ if (cap1_el_ix > cap2_el_ix) {
43
+ return 1;
44
+ } else if (cap1_el_ix < cap2_el_ix) {
45
+ return -1;
46
+ }
47
+
48
+ // next try length of match
49
+ len1 = capture1.end - cap1_ix;
50
+ len2 = capture2.end - cap2_ix;
51
+ if (len1 > len2) {
52
+ return 1;
53
+ } else if (len1 < len2) {
54
+ return -1;
55
+ }
56
+ }
57
+ return 0;
58
+ }
59
+
60
+ private static int sorted_ix(List<Integer> ixs, int val) {
61
+ if (ixs.size() == 0)
62
+ return 0;
63
+ if (val < ixs.get(0))
64
+ return 0;
65
+ if (ixs.size() == 1) {
66
+ if (val > ixs.get(0))
67
+ return 1;
68
+ else
69
+ return 0;
70
+ } else {
71
+ for (int i = 0; i < ixs.size() - 1; i++) {
72
+ if (val > ixs.get(i) && val < ixs.get(i + 1))
73
+ return i + 1;
74
+ }
75
+ return ixs.size();
76
+ }
77
+ }
78
+
79
+ // this method is mainly for testing in the Ruby specs
80
+ public static String testRank(String selector_a, String selector_b, String scope_string) {
81
+ Match m1 = match(selector_a, scope_string);
82
+ Match m2 = match(selector_b, scope_string);
83
+ int r = compareMatch(scope_string, m1, m2);
84
+ if (r > 0) {
85
+ return selector_a;
86
+ } else if (r == 0) {
87
+ return selector_a + " == " + selector_b;
88
+ } else {
89
+ return selector_b;
90
+ }
91
+ }
92
+
93
+ public static boolean testMatch(String selectorString, String scopeString) {
94
+ Match m = getMatch(selectorString, scopeString);
95
+ return (m != null);
96
+ }
97
+
98
+ public static Match getMatch(String selectorString, String scopeString) {
99
+ Match m = match(selectorString, scopeString);
100
+ if (m != null) {
101
+ // System.out.printf("%d\n", m.numCaptures());
102
+ Range firstCapture = m.getCapture(0);
103
+ // System.out.printf("test_match('%s', '%s') == %d\n", selectorString, scopeString, firstCapture.start);
104
+ } else {
105
+ // System.out.printf("test_match('%s', '%s') == null\n", selectorString, scopeString);
106
+ }
107
+ return m;
108
+ }
109
+
110
+ public static Match match(String selectorString, String scopeString) {
111
+ List<ScopeMatcher> matchers = ScopeMatcher.compile(selectorString);
112
+ for (ScopeMatcher matcher : matchers) {
113
+ Match m;
114
+ if ((m = testMatchRe(matcher.pos_rx, matcher.neg_rxs, scopeString)) != null)
115
+ return m;
116
+ }
117
+ return null;
118
+ }
119
+
120
+ public static List<ScopeMatcher> compile(String selectorString) {
121
+ List<ScopeMatcher> ms = new ArrayList<ScopeMatcher>();
122
+ // FIXME should validate and throw UTF8 error if bad.
123
+ String[] scopeOrs1 = selectorString.split(",");
124
+ // System.out.printf("match: selector: '%s'\n", selectorString);
125
+ for (String selectorString1 : scopeOrs1) {
126
+ ScopeMatcher m = new ScopeMatcher();
127
+ m.neg_rxs = new ArrayList<Rx>();
128
+ String[] positivesAndNegatives = selectorString1.split(" -");
129
+ for (String subSelectorString : positivesAndNegatives) {
130
+ if (m.pos_rx == null) {
131
+ String s1 = subSelectorString.trim().replaceAll("\\.", "\\\\.");
132
+ String s2 = s1.replaceAll(" ", ").* .*(");
133
+ // System.out.printf("positive '%s'\n", "(" + s2 + ")");
134
+ m.pos_rx = Rx.createRx("^(?:.*[^A-Za-z])?(" + s2 + ")(?:[^A-Za-z].*)?$");
135
+ } else {
136
+ String s1 = subSelectorString.trim().replaceAll("\\.", "\\\\.");
137
+ String s2 = s1.trim().replaceAll(" ", ".* .*");
138
+ // System.out.printf("negative '%s'\n", s2);
139
+ m.neg_rxs.add(Rx.createRx(s2));
140
+ }
141
+ }
142
+ ms.add(m);
143
+ }
144
+ return ms;
145
+ }
146
+
147
+ public static Match testMatchRe(Rx positiveSelectorRegex, List<Rx> negativeSelectorRegexes, String scopeString) {
148
+ Match m = positiveSelectorRegex.search(scopeString, 0, scopeString.length());
149
+ if (m != null) {
150
+ for (Rx negRx : negativeSelectorRegexes) {
151
+ Match m1 = negRx.search(scopeString, 0, scopeString.length());
152
+ if (m1 != null) {
153
+ return null;
154
+ }
155
+ }
156
+ return m;
157
+ } else {
158
+ return null;
159
+ }
160
+ }
161
+
162
+ }
@@ -0,0 +1,110 @@
1
+ /*******************************************************************************
2
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
3
+ * All rights reserved. This program and the accompanying materials
4
+ * are made available under the terms of the Eclipse Public License v1.0
5
+ * which accompanies this distribution, and is available at
6
+ * http://www.eclipse.org/legal/epl-v10.html
7
+ *
8
+ * Contributors:
9
+ * IBM Corporation - initial API and implementation
10
+ *******************************************************************************/
11
+ package com.redcareditor.mate;
12
+
13
+ import java.util.HashMap;
14
+ import java.util.Iterator;
15
+ import java.util.Map;
16
+
17
+ import org.eclipse.swt.graphics.Color;
18
+ import org.eclipse.swt.graphics.RGB;
19
+ import org.eclipse.swt.widgets.Display;
20
+
21
+ import org.eclipse.jface.text.source.ISharedTextColors;
22
+
23
+ /*
24
+ * @see org.eclipse.jface.text.source.ISharedTextColors
25
+ * @since 2.1
26
+ */
27
+ class SharedTextColors implements ISharedTextColors {
28
+
29
+ /** The display table. */
30
+ private Map fDisplayTable;
31
+
32
+ /** Creates an returns a shared color manager. */
33
+ public SharedTextColors() {
34
+ super();
35
+ }
36
+
37
+ /*
38
+ * @see ISharedTextColors#getColor(RGB)
39
+ */
40
+ public Color getColor(RGB rgb) {
41
+ if (rgb == null)
42
+ return null;
43
+
44
+ if (fDisplayTable == null)
45
+ fDisplayTable= new HashMap(2);
46
+
47
+ final Display display= Display.getCurrent();
48
+
49
+ Map colorTable= (Map) fDisplayTable.get(display);
50
+ if (colorTable == null) {
51
+ colorTable= new HashMap(10);
52
+ fDisplayTable.put(display, colorTable);
53
+ display.disposeExec(new Runnable() {
54
+ public void run() {
55
+ dispose(display);
56
+ }
57
+ });
58
+ }
59
+
60
+ Color color= (Color) colorTable.get(rgb);
61
+ if (color == null) {
62
+ color= new Color(display, rgb);
63
+ colorTable.put(rgb, color);
64
+ }
65
+
66
+ return color;
67
+ }
68
+
69
+ /*
70
+ * @see ISharedTextColors#dispose()
71
+ */
72
+ public void dispose() {
73
+ if (fDisplayTable == null)
74
+ return;
75
+
76
+ Iterator iter= fDisplayTable.values().iterator();
77
+ while (iter.hasNext())
78
+ dispose((Map)iter.next());
79
+ fDisplayTable= null;
80
+ }
81
+
82
+ /**
83
+ * Disposes the colors for the given display.
84
+ *
85
+ * @param display the display for which to dispose the colors
86
+ * @since 3.3
87
+ */
88
+ private void dispose(Display display) {
89
+ if (fDisplayTable != null)
90
+ dispose((Map)fDisplayTable.remove(display));
91
+ }
92
+
93
+ /**
94
+ * Disposes the given color table.
95
+ *
96
+ * @param colorTable the color table that maps <code>RGB</code> to <code>Color</code>
97
+ * @since 3.3
98
+ */
99
+ private void dispose(Map colorTable) {
100
+ if (colorTable == null)
101
+ return;
102
+
103
+ Iterator iter= colorTable.values().iterator();
104
+ while (iter.hasNext())
105
+ ((Color) iter.next()).dispose();
106
+
107
+ colorTable.clear();
108
+ }
109
+
110
+ }
@@ -0,0 +1,20 @@
1
+ package com.redcareditor.mate;
2
+
3
+ import java.util.List;
4
+ import java.util.Map;
5
+
6
+ import com.redcareditor.onig.Rx;
7
+ import com.redcareditor.plist.Dict;
8
+
9
+ public class SinglePattern extends Pattern {
10
+ public Rx match;
11
+ public Map<Integer, String> captures;
12
+
13
+ public SinglePattern(List<Pattern> grammarPatterns, Dict dict) {
14
+ name = dict.getString("name");
15
+ match = Rx.createRx(dict.getString("match"));
16
+ captures = makeCapturesFromPlist(dict.getDictionary("captures"));
17
+ setDisabled(dict);
18
+ grammarPatterns.add(this);
19
+ }
20
+ }
@@ -0,0 +1,395 @@
1
+ /*******************************************************************************
2
+ * Copyright (c) 2006, 2009 Wind River Systems, Inc., IBM Corporation and others.
3
+ * All rights reserved. This program and the accompanying materials
4
+ * are made available under the terms of the Eclipse Public License v1.0
5
+ * which accompanies this distribution, and is available at
6
+ * http://www.eclipse.org/legal/epl-v10.html
7
+ *
8
+ * Contributors:
9
+ * Anton Leherbauer (Wind River Systems) - initial API and implementation - https://bugs.eclipse.org/bugs/show_bug.cgi?id=22712
10
+ * Anton Leherbauer (Wind River Systems) - [painting] Long lines take too long to display when "Show Whitespace Characters" is enabled - https://bugs.eclipse.org/bugs/show_bug.cgi?id=196116
11
+ * Anton Leherbauer (Wind River Systems) - [painting] Whitespace characters not drawn when scrolling to right slowly - https://bugs.eclipse.org/bugs/show_bug.cgi?id=206633
12
+ * Tom Eicher (Avaloq Evolution AG) - block selection mode
13
+ *******************************************************************************/
14
+ package com.redcareditor.mate;
15
+
16
+ import org.eclipse.swt.custom.StyleRange;
17
+ import org.eclipse.swt.custom.StyledText;
18
+ import org.eclipse.swt.custom.StyledTextContent;
19
+ import org.eclipse.swt.events.PaintEvent;
20
+ import org.eclipse.swt.events.PaintListener;
21
+ import org.eclipse.swt.graphics.Color;
22
+ import org.eclipse.swt.graphics.FontMetrics;
23
+ import org.eclipse.swt.graphics.GC;
24
+ import org.eclipse.swt.graphics.Point;
25
+ import org.eclipse.jface.text.IDocument;
26
+ import org.eclipse.jface.text.IPainter;
27
+ import org.eclipse.jface.text.ITextViewer;
28
+ import org.eclipse.jface.text.IRegion;
29
+ import org.eclipse.jface.text.BadLocationException;
30
+ import org.eclipse.jface.text.ITextViewerExtension5;
31
+ import org.eclipse.jface.text.IPaintPositionManager;
32
+
33
+
34
+ /**
35
+ * A painter for drawing visible characters for (invisible) whitespace
36
+ * characters.
37
+ *
38
+ * @since 3.3
39
+ */
40
+ public class WhitespaceCharacterPainter implements IPainter, PaintListener {
41
+
42
+ private static final char SPACE_SIGN= '\u00b7';
43
+ private static final char IDEOGRAPHIC_SPACE_SIGN= '\u00b0';
44
+ private static final char TAB_SIGN= '\u00bb';
45
+ private static final char CARRIAGE_RETURN_SIGN= '\u00a4';
46
+ private static final char LINE_FEED_SIGN= '\u00ac';
47
+
48
+ /** Indicates whether this painter is active. */
49
+ private boolean fIsActive= false;
50
+ /** The source viewer this painter is attached to. */
51
+ private ITextViewer fTextViewer;
52
+ /** The viewer's widget. */
53
+ private StyledText fTextWidget;
54
+ /** Tells whether the advanced graphics sub system is available. */
55
+ private final boolean fIsAdvancedGraphicsPresent;
56
+
57
+ /**
58
+ * Creates a new painter for the given text viewer.
59
+ *
60
+ * @param textViewer the text viewer the painter should be attached to
61
+ */
62
+ public WhitespaceCharacterPainter(ITextViewer textViewer) {
63
+ super();
64
+ fTextViewer= textViewer;
65
+ fTextWidget= textViewer.getTextWidget();
66
+ GC gc= new GC(fTextWidget);
67
+ gc.setAdvanced(true);
68
+ fIsAdvancedGraphicsPresent= gc.getAdvanced();
69
+ gc.dispose();
70
+ }
71
+
72
+ /*
73
+ * @see org.eclipse.jface.text.IPainter#dispose()
74
+ */
75
+ public void dispose() {
76
+ fTextViewer= null;
77
+ fTextWidget= null;
78
+ }
79
+
80
+ /*
81
+ * @see org.eclipse.jface.text.IPainter#paint(int)
82
+ */
83
+ public void paint(int reason) {
84
+ IDocument document= fTextViewer.getDocument();
85
+ if (document == null) {
86
+ deactivate(false);
87
+ return;
88
+ }
89
+ if (!fIsActive) {
90
+ fIsActive= true;
91
+ fTextWidget.addPaintListener(this);
92
+ redrawAll();
93
+ } else if (reason == CONFIGURATION || reason == INTERNAL) {
94
+ redrawAll();
95
+ } else if (reason == TEXT_CHANGE) {
96
+ // redraw current line only
97
+ try {
98
+ IRegion lineRegion =
99
+ document.getLineInformationOfOffset(getDocumentOffset(fTextWidget.getCaretOffset()));
100
+ int widgetOffset= getWidgetOffset(lineRegion.getOffset());
101
+ int charCount= fTextWidget.getCharCount();
102
+ int redrawLength= Math.min(lineRegion.getLength(), charCount - widgetOffset);
103
+ if (widgetOffset >= 0 && redrawLength > 0) {
104
+ fTextWidget.redrawRange(widgetOffset, redrawLength, true);
105
+ }
106
+ } catch (BadLocationException e) {
107
+ // ignore
108
+ }
109
+ }
110
+ }
111
+
112
+ /*
113
+ * @see org.eclipse.jface.text.IPainter#deactivate(boolean)
114
+ */
115
+ public void deactivate(boolean redraw) {
116
+ if (fIsActive) {
117
+ fIsActive= false;
118
+ fTextWidget.removePaintListener(this);
119
+ if (redraw) {
120
+ redrawAll();
121
+ }
122
+ }
123
+ }
124
+
125
+ /*
126
+ * @see org.eclipse.jface.text.IPainter#setPositionManager(org.eclipse.jface.text.IPaintPositionManager)
127
+ */
128
+ public void setPositionManager(IPaintPositionManager manager) {
129
+ // no need for a position manager
130
+ }
131
+
132
+ /*
133
+ * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
134
+ */
135
+ public void paintControl(PaintEvent event) {
136
+ if (fTextWidget != null) {
137
+ handleDrawRequest(event.gc, event.x, event.y, event.width, event.height);
138
+ }
139
+ }
140
+
141
+ /*
142
+ * Draw characters in view range.
143
+ */
144
+ private void handleDrawRequest(GC gc, int x, int y, int w, int h) {
145
+ int startLine= fTextWidget.getLineIndex(y);
146
+ int endLine= fTextWidget.getLineIndex(y + h - 1);
147
+ if (startLine <= endLine && startLine < fTextWidget.getLineCount()) {
148
+ if (fIsAdvancedGraphicsPresent) {
149
+ int alpha= gc.getAlpha();
150
+ gc.setAlpha(100);
151
+ drawLineRange(gc, startLine, endLine, x, w);
152
+ gc.setAlpha(alpha);
153
+ } else
154
+ drawLineRange(gc, startLine, endLine, x, w);
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Draw the given line range.
160
+ *
161
+ * @param gc the GC
162
+ * @param startLine first line number
163
+ * @param endLine last line number (inclusive)
164
+ * @param x the X-coordinate of the drawing range
165
+ * @param w the width of the drawing range
166
+ */
167
+ private void drawLineRange(GC gc, int startLine, int endLine, int x, int w) {
168
+ final int viewPortWidth= fTextWidget.getClientArea().width;
169
+ for (int line= startLine; line <= endLine; line++) {
170
+ int lineOffset= fTextWidget.getOffsetAtLine(line);
171
+ // line end offset including line delimiter
172
+ int lineEndOffset;
173
+ if (line < fTextWidget.getLineCount() - 1) {
174
+ lineEndOffset= fTextWidget.getOffsetAtLine(line + 1);
175
+ } else {
176
+ lineEndOffset= fTextWidget.getCharCount();
177
+ }
178
+ // line length excluding line delimiter
179
+ int lineLength= lineEndOffset - lineOffset;
180
+ while (lineLength > 0) {
181
+ char c= fTextWidget.getTextRange(lineOffset + lineLength - 1, 1).charAt(0);
182
+ if (c != '\r' && c != '\n') {
183
+ break;
184
+ }
185
+ --lineLength;
186
+ }
187
+ // compute coordinates of last character on line
188
+ Point endOfLine= fTextWidget.getLocationAtOffset(lineOffset + lineLength);
189
+ if (x - endOfLine.x > viewPortWidth) {
190
+ // line is not visible
191
+ continue;
192
+ }
193
+ // Y-coordinate of line
194
+ int y= fTextWidget.getLinePixel(line);
195
+ // compute first visible char offset
196
+ int startOffset;
197
+ try {
198
+ startOffset= fTextWidget.getOffsetAtLocation(new Point(x, y)) - 1;
199
+ if (startOffset - 2 <= lineOffset) {
200
+ startOffset= lineOffset;
201
+ }
202
+ } catch (IllegalArgumentException iae) {
203
+ startOffset= lineOffset;
204
+ }
205
+ // compute last visible char offset
206
+ int endOffset;
207
+ if (x + w >= endOfLine.x) {
208
+ // line end is visible
209
+ endOffset= lineEndOffset;
210
+ } else {
211
+ try {
212
+ endOffset= fTextWidget.getOffsetAtLocation(new Point(x + w - 1, y)) + 1;
213
+ if (endOffset + 2 >= lineEndOffset) {
214
+ endOffset= lineEndOffset;
215
+ }
216
+ } catch (IllegalArgumentException iae) {
217
+ endOffset= lineEndOffset;
218
+ }
219
+ }
220
+ // draw character range
221
+ if (endOffset > startOffset) {
222
+ drawCharRange(gc, startOffset, endOffset);
223
+ }
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Draw characters of content range.
229
+ *
230
+ * @param gc the GC
231
+ * @param startOffset inclusive start index
232
+ * @param endOffset exclusive end index
233
+ */
234
+ private void drawCharRange(GC gc, int startOffset, int endOffset) {
235
+ StyledTextContent content= fTextWidget.getContent();
236
+ int length= endOffset - startOffset;
237
+ String text= content.getTextRange(startOffset, length);
238
+ StyleRange styleRange= null;
239
+ Color fg= null;
240
+ StringBuffer visibleChar= new StringBuffer(10);
241
+ for (int textOffset= 0; textOffset <= length; ++textOffset) {
242
+ int delta= 0;
243
+ boolean eol= false;
244
+ if (textOffset < length) {
245
+ delta= 1;
246
+ char c= text.charAt(textOffset);
247
+ switch (c) {
248
+ //case ' ' :
249
+ // visibleChar.append(SPACE_SIGN);
250
+ // 'continue' would improve performance but may produce drawing errors
251
+ // for long runs of space if width of space and dot differ
252
+ // break;
253
+ case '\u3000' : // ideographic whitespace
254
+ visibleChar.append(IDEOGRAPHIC_SPACE_SIGN);
255
+ // 'continue' would improve performance but may produce drawing errors
256
+ // for long runs of space if width of space and dot differ
257
+ break;
258
+ case '\t' :
259
+ visibleChar.append(TAB_SIGN);
260
+ break;
261
+ case '\r' :
262
+ visibleChar.append(CARRIAGE_RETURN_SIGN);
263
+ if (textOffset >= length - 1 || text.charAt(textOffset + 1) != '\n') {
264
+ eol= true;
265
+ break;
266
+ }
267
+ continue;
268
+ case '\n' :
269
+ visibleChar.append(LINE_FEED_SIGN);
270
+ eol= true;
271
+ break;
272
+ default :
273
+ delta= 0;
274
+ break;
275
+ }
276
+ }
277
+ if (visibleChar.length() > 0) {
278
+ int widgetOffset= startOffset + textOffset - visibleChar.length() + delta;
279
+ if (!eol || !isFoldedLine(content.getLineAtOffset(widgetOffset))) {
280
+ /*
281
+ * Block selection is drawn using alpha and no selection-inverting
282
+ * takes place, we always draw as 'unselected' in block selection mode.
283
+ */
284
+ if (!fTextWidget.getBlockSelection() && isOffsetSelected(fTextWidget, widgetOffset)) {
285
+ fg= fTextWidget.getSelectionForeground();
286
+ } else if (styleRange == null || styleRange.start + styleRange.length <= widgetOffset) {
287
+ styleRange= fTextWidget.getStyleRangeAtOffset(widgetOffset);
288
+ if (styleRange == null || styleRange.foreground == null) {
289
+ fg= fTextWidget.getForeground();
290
+ } else {
291
+ fg= styleRange.foreground;
292
+ }
293
+ }
294
+ draw(gc, widgetOffset, visibleChar.toString(), fg);
295
+ }
296
+ visibleChar.delete(0, visibleChar.length());
297
+ }
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Returns <code>true</code> if <code>offset</code> is selection in <code>widget</code>,
303
+ * <code>false</code> otherwise.
304
+ *
305
+ * @param widget the widget
306
+ * @param offset the offset
307
+ * @return <code>true</code> if <code>offset</code> is selection, <code>false</code> otherwise
308
+ * @since 3.5
309
+ */
310
+ private static final boolean isOffsetSelected(StyledText widget, int offset) {
311
+ Point selection= widget.getSelection();
312
+ return offset >= selection.x && offset < selection.y;
313
+ }
314
+
315
+ /**
316
+ * Check if the given widget line is a folded line.
317
+ *
318
+ * @param widgetLine the widget line number
319
+ * @return <code>true</code> if the line is folded
320
+ */
321
+ private boolean isFoldedLine(int widgetLine) {
322
+ if (fTextViewer instanceof ITextViewerExtension5) {
323
+ ITextViewerExtension5 extension= (ITextViewerExtension5)fTextViewer;
324
+ int modelLine= extension.widgetLine2ModelLine(widgetLine);
325
+ int widgetLine2= extension.modelLine2WidgetLine(modelLine + 1);
326
+ return widgetLine2 == -1;
327
+ }
328
+ return false;
329
+ }
330
+
331
+ /**
332
+ * Redraw all of the text widgets visible content.
333
+ */
334
+ private void redrawAll() {
335
+ fTextWidget.redraw();
336
+ }
337
+
338
+ /**
339
+ * Draw string at widget offset.
340
+ *
341
+ * @param gc the GC
342
+ * @param offset the widget offset
343
+ * @param s the string to be drawn
344
+ * @param fg the foreground color
345
+ */
346
+ private void draw(GC gc, int offset, String s, Color fg) {
347
+ // Compute baseline delta (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=165640)
348
+ int baseline= fTextWidget.getBaseline(offset);
349
+ FontMetrics fontMetrics= gc.getFontMetrics();
350
+ int fontBaseline= fontMetrics.getAscent() + fontMetrics.getLeading();
351
+ int baslineDelta= baseline - fontBaseline;
352
+
353
+ Point pos= fTextWidget.getLocationAtOffset(offset);
354
+ gc.setForeground(fg);
355
+ gc.drawString(s, pos.x, pos.y + baslineDelta, true);
356
+ }
357
+
358
+ /**
359
+ * Convert a document offset to the corresponding widget offset.
360
+ *
361
+ * @param documentOffset the document offset
362
+ * @return widget offset
363
+ */
364
+ private int getWidgetOffset(int documentOffset) {
365
+ if (fTextViewer instanceof ITextViewerExtension5) {
366
+ ITextViewerExtension5 extension= (ITextViewerExtension5)fTextViewer;
367
+ return extension.modelOffset2WidgetOffset(documentOffset);
368
+ }
369
+ IRegion visible= fTextViewer.getVisibleRegion();
370
+ int widgetOffset= documentOffset - visible.getOffset();
371
+ if (widgetOffset > visible.getLength()) {
372
+ return -1;
373
+ }
374
+ return widgetOffset;
375
+ }
376
+
377
+ /**
378
+ * Convert a widget offset to the corresponding document offset.
379
+ *
380
+ * @param widgetOffset the widget offset
381
+ * @return document offset
382
+ */
383
+ private int getDocumentOffset(int widgetOffset) {
384
+ if (fTextViewer instanceof ITextViewerExtension5) {
385
+ ITextViewerExtension5 extension= (ITextViewerExtension5)fTextViewer;
386
+ return extension.widgetOffset2ModelOffset(widgetOffset);
387
+ }
388
+ IRegion visible= fTextViewer.getVisibleRegion();
389
+ if (widgetOffset > visible.getLength()) {
390
+ return -1;
391
+ }
392
+ return widgetOffset + visible.getOffset();
393
+ }
394
+
395
+ }