redcar-javamateview 0.1-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|