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.
- data/LICENSE +34 -0
- data/README +58 -0
- data/Rakefile +94 -0
- data/lib/javamateview.rb +41 -0
- data/lib/javamateview/example.rb +334 -0
- data/lib/javamateview/jar/java-mateview.jar +0 -0
- data/lib/javamateview/jcodings.jar +0 -0
- data/lib/javamateview/jdom.jar +0 -0
- data/lib/javamateview/joni.jar +0 -0
- data/spec/onig/match_spec.rb +50 -0
- data/spec/parsing/dynamic_parsing_spec.rb +172 -0
- data/spec/parsing/static_parsing_spec.rb +476 -0
- data/spec/spec_helper.rb +33 -0
- data/src/com/redcareditor/mate/Bundle.java +81 -0
- data/src/com/redcareditor/mate/DoublePattern.java +89 -0
- data/src/com/redcareditor/mate/Grammar.java +129 -0
- data/src/com/redcareditor/mate/IAnnotationAreaListener.java +7 -0
- data/src/com/redcareditor/mate/IGrammarListener.java +5 -0
- data/src/com/redcareditor/mate/IncludePattern.java +10 -0
- data/src/com/redcareditor/mate/LineNumberRulerColumn.java +922 -0
- data/src/com/redcareditor/mate/Marker.java +22 -0
- data/src/com/redcareditor/mate/MateText.java +697 -0
- data/src/com/redcareditor/mate/ParseThunk.java +71 -0
- data/src/com/redcareditor/mate/Parser.java +627 -0
- data/src/com/redcareditor/mate/ParserScheduler.java +237 -0
- data/src/com/redcareditor/mate/Pattern.java +152 -0
- data/src/com/redcareditor/mate/RangeSet.java +91 -0
- data/src/com/redcareditor/mate/Scanner.java +178 -0
- data/src/com/redcareditor/mate/Scope.java +534 -0
- data/src/com/redcareditor/mate/ScopeMatcher.java +162 -0
- data/src/com/redcareditor/mate/SharedTextColors.java +110 -0
- data/src/com/redcareditor/mate/SinglePattern.java +20 -0
- data/src/com/redcareditor/mate/WhitespaceCharacterPainter.java +395 -0
- data/src/com/redcareditor/mate/colouring/Colourer.java +16 -0
- data/src/com/redcareditor/mate/colouring/swt/MarginPaintListener.java +62 -0
- data/src/com/redcareditor/mate/colouring/swt/SwtColourer.java +501 -0
- data/src/com/redcareditor/mate/document/MateDocument.java +15 -0
- data/src/com/redcareditor/mate/document/MateTextFactory.java +9 -0
- data/src/com/redcareditor/mate/document/MateTextLocation.java +8 -0
- data/src/com/redcareditor/mate/document/MateTextLocationComparator.java +17 -0
- data/src/com/redcareditor/mate/document/MateTextRange.java +18 -0
- data/src/com/redcareditor/mate/document/swt/SwtMateDocument.java +143 -0
- data/src/com/redcareditor/mate/document/swt/SwtMateTextLocation.java +88 -0
- data/src/com/redcareditor/mate/document/swt/SwtMateTextRange.java +92 -0
- data/src/com/redcareditor/mate/document/swt/SwtScopePositionUpdater.java +90 -0
- data/src/com/redcareditor/mate/undo/MateTextUndoManager.java +11 -0
- data/src/com/redcareditor/mate/undo/swt/SwtMateTextUndoManager.java +166 -0
- data/src/com/redcareditor/onig/Match.java +212 -0
- data/src/com/redcareditor/onig/NullMatch.java +57 -0
- data/src/com/redcareditor/onig/NullRx.java +29 -0
- data/src/com/redcareditor/onig/Range.java +45 -0
- data/src/com/redcareditor/onig/Rx.java +167 -0
- data/src/com/redcareditor/plist/Dict.java +119 -0
- data/src/com/redcareditor/plist/PlistNode.java +52 -0
- data/src/com/redcareditor/plist/PlistPropertyLoader.java +44 -0
- data/src/com/redcareditor/theme/ScopeSelector.java +39 -0
- data/src/com/redcareditor/theme/Theme.java +122 -0
- data/src/com/redcareditor/theme/ThemeManager.java +41 -0
- data/src/com/redcareditor/theme/ThemeSetting.java +78 -0
- data/src/com/redcareditor/util/FileUtility.java +64 -0
- data/src/com/redcareditor/util/SingleLineFormatter.java +11 -0
- data/src/com/redcareditor/util/swt/ColourUtil.java +56 -0
- data/src/ruby/java-mateview.rb +68 -0
- data/test/com/redcareditor/mate/BundleTest.java +33 -0
- data/test/com/redcareditor/mate/EmptyRangeSetTest.java +27 -0
- data/test/com/redcareditor/mate/FilledRangeSetTest.java +82 -0
- data/test/com/redcareditor/mate/GrammarTest.java +158 -0
- data/test/com/redcareditor/mate/MateTextTest.java +35 -0
- data/test/com/redcareditor/mate/ScopeMatcherMatchingTest.java +55 -0
- data/test/com/redcareditor/mate/ScopeMatcherRankingTest.java +40 -0
- data/test/com/redcareditor/onig/RxTest.java +54 -0
- data/test/com/redcareditor/plist/DictTest.java +33 -0
- data/test/com/redcareditor/theme/RailsCastThemeTest.java +37 -0
- data/test/com/redcareditor/theme/ScopeSelectorTest.java +38 -0
- data/test/com/redcareditor/theme/ThemeManagerTest.java +29 -0
- data/test/com/redcareditor/util/swt/ColourUtilTest.java +17 -0
- 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
|
+
}
|