redcar-javamateview 0.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +142 -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
+ }