|
|
|
@ -9,6 +9,24 @@
|
|
|
|
|
*/ |
|
|
|
|
package com.fr.design.gui.syntax.ui.rsyntaxtextarea; |
|
|
|
|
|
|
|
|
|
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.TokenUtils.TokenSubList; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.folding.FoldManager; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.Gutter; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.RTextArea; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.RTextScrollPane; |
|
|
|
|
|
|
|
|
|
import javax.swing.JLabel; |
|
|
|
|
import javax.swing.JViewport; |
|
|
|
|
import javax.swing.SwingConstants; |
|
|
|
|
import javax.swing.UIManager; |
|
|
|
|
import javax.swing.text.BadLocationException; |
|
|
|
|
import javax.swing.text.Caret; |
|
|
|
|
import javax.swing.text.Document; |
|
|
|
|
import javax.swing.text.Element; |
|
|
|
|
import javax.swing.text.Position; |
|
|
|
|
import javax.swing.text.Segment; |
|
|
|
|
import javax.swing.text.TabExpander; |
|
|
|
|
import javax.swing.text.View; |
|
|
|
|
import java.awt.Color; |
|
|
|
|
import java.awt.Container; |
|
|
|
|
import java.awt.Point; |
|
|
|
@ -16,23 +34,9 @@ import java.awt.Rectangle;
|
|
|
|
|
import java.awt.Shape; |
|
|
|
|
import java.awt.Toolkit; |
|
|
|
|
import java.util.Map; |
|
|
|
|
import java.util.Objects; |
|
|
|
|
import java.util.regex.Pattern; |
|
|
|
|
import java.util.regex.PatternSyntaxException; |
|
|
|
|
import javax.swing.*; |
|
|
|
|
import javax.swing.text.BadLocationException; |
|
|
|
|
import javax.swing.text.Caret; |
|
|
|
|
import javax.swing.text.Document; |
|
|
|
|
import javax.swing.text.Element; |
|
|
|
|
import javax.swing.text.Position; |
|
|
|
|
import javax.swing.text.Segment; |
|
|
|
|
import javax.swing.text.TabExpander; |
|
|
|
|
import javax.swing.text.View; |
|
|
|
|
|
|
|
|
|
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.TokenUtils.TokenSubList; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.folding.FoldManager; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.Gutter; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.RTextArea; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.RTextScrollPane; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -122,7 +126,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
/** |
|
|
|
|
* Used internally. |
|
|
|
|
*/ |
|
|
|
|
private static final char[] JS_KEYWORD_RETURN = { 'r', 'e', 't', 'u', 'r', 'n' }; |
|
|
|
|
private static final char[] JS_KEYWORD_RETURN = {'r', 'e', 't', 'u', 'r', 'n'}; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Used internally. |
|
|
|
@ -146,10 +150,10 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
public static final String escapeForHtml(String s, |
|
|
|
|
String newlineReplacement, boolean inPreBlock) { |
|
|
|
|
|
|
|
|
|
if (s==null) { |
|
|
|
|
if (s == null) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
if (newlineReplacement==null) { |
|
|
|
|
if (newlineReplacement == null) { |
|
|
|
|
newlineReplacement = ""; |
|
|
|
|
} |
|
|
|
|
final String tabString = " "; |
|
|
|
@ -157,14 +161,13 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
|
|
|
|
|
StringBuilder sb = new StringBuilder(); |
|
|
|
|
|
|
|
|
|
for (int i=0; i<s.length(); i++) { |
|
|
|
|
for (int i = 0; i < s.length(); i++) { |
|
|
|
|
char ch = s.charAt(i); |
|
|
|
|
switch (ch) { |
|
|
|
|
case ' ': |
|
|
|
|
if (inPreBlock || !lastWasSpace) { |
|
|
|
|
sb.append(' '); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
sb.append(" "); |
|
|
|
|
} |
|
|
|
|
lastWasSpace = true; |
|
|
|
@ -208,8 +211,8 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
* @return The rendering hints, or <code>null</code> if they cannot be |
|
|
|
|
* determined. |
|
|
|
|
*/ |
|
|
|
|
public static Map<?,?> getDesktopAntiAliasHints() { |
|
|
|
|
return (Map<?,?>)Toolkit.getDefaultToolkit(). |
|
|
|
|
public static Map<?, ?> getDesktopAntiAliasHints() { |
|
|
|
|
return (Map<?, ?>) Toolkit.getDefaultToolkit(). |
|
|
|
|
getDesktopProperty("awt.font.desktophints"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -223,7 +226,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
public static Color getFoldedLineBottomColor(RSyntaxTextArea textArea) { |
|
|
|
|
Color color = Color.gray; |
|
|
|
|
Gutter gutter = RSyntaxUtilities.getGutter(textArea); |
|
|
|
|
if (gutter!=null) { |
|
|
|
|
if (gutter != null) { |
|
|
|
|
color = gutter.getFoldIndicatorForeground(); |
|
|
|
|
} |
|
|
|
|
return color; |
|
|
|
@ -245,7 +248,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
if (parent instanceof JViewport) { |
|
|
|
|
parent = parent.getParent(); |
|
|
|
|
if (parent instanceof RTextScrollPane) { |
|
|
|
|
RTextScrollPane sp = (RTextScrollPane)parent; |
|
|
|
|
RTextScrollPane sp = (RTextScrollPane) parent; |
|
|
|
|
gutter = sp.getGutter(); // Should always be non-null
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -267,7 +270,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
// This property is defined by all standard LaFs, even Nimbus (!),
|
|
|
|
|
// but you never know what crazy LaFs there are...
|
|
|
|
|
Color fg = UIManager.getColor("Label.foreground"); |
|
|
|
|
if (fg==null) { |
|
|
|
|
if (fg == null) { |
|
|
|
|
fg = new JLabel().getForeground(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -286,7 +289,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
public static String getLeadingWhitespace(String text) { |
|
|
|
|
int count = 0; |
|
|
|
|
int len = text.length(); |
|
|
|
|
while (count<len && RSyntaxUtilities.isWhitespace(text.charAt(count))) { |
|
|
|
|
while (count < len && RSyntaxUtilities.isWhitespace(text.charAt(count))) { |
|
|
|
|
count++; |
|
|
|
|
} |
|
|
|
|
return text.substring(0, count); |
|
|
|
@ -310,7 +313,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
Element elem = root.getElement(line); |
|
|
|
|
int startOffs = elem.getStartOffset(); |
|
|
|
|
int endOffs = elem.getEndOffset() - 1; |
|
|
|
|
String text = doc.getText(startOffs, endOffs-startOffs); |
|
|
|
|
String text = doc.getText(startOffs, endOffs - startOffs); |
|
|
|
|
return getLeadingWhitespace(text); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -319,7 +322,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
Element map = d.getDefaultRootElement(); |
|
|
|
|
int index = map.getElementIndex(offs); |
|
|
|
|
Element elem = map.getElement(index); |
|
|
|
|
if ((offs>=elem.getStartOffset()) && (offs<elem.getEndOffset())) { |
|
|
|
|
if ((offs >= elem.getStartOffset()) && (offs < elem.getEndOffset())) { |
|
|
|
|
return elem; |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
@ -358,13 +361,14 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
int x0) |
|
|
|
|
throws BadLocationException { |
|
|
|
|
|
|
|
|
|
RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument(); |
|
|
|
|
RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument(); |
|
|
|
|
|
|
|
|
|
// Ensure p0 and p1 are valid document positions.
|
|
|
|
|
if (p0<0) |
|
|
|
|
if (p0 < 0) { |
|
|
|
|
throw new BadLocationException("Invalid document position", p0); |
|
|
|
|
else if (p1>doc.getLength()) |
|
|
|
|
} else if (p1 > doc.getLength()) { |
|
|
|
|
throw new BadLocationException("Invalid document position", p1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Ensure p0 and p1 are in the same line, and get the start/end
|
|
|
|
|
// offsets for that line.
|
|
|
|
@ -373,9 +377,10 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
// We do ">1" because p1 might be the first position on the next line
|
|
|
|
|
// or the last position on the previous one.
|
|
|
|
|
// if (lineNum!=map.getElementIndex(p1))
|
|
|
|
|
if (Math.abs(lineNum-map.getElementIndex(p1))>1) |
|
|
|
|
if (Math.abs(lineNum - map.getElementIndex(p1)) > 1) { |
|
|
|
|
throw new IllegalArgumentException("p0 and p1 are not on the " + |
|
|
|
|
"same line (" + p0 + ", " + p1 + ")."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Get the token list.
|
|
|
|
|
Token t = doc.getTokenListForLine(lineNum); |
|
|
|
@ -410,7 +415,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
public static Point getMatchingBracketPosition(RSyntaxTextArea textArea, |
|
|
|
|
Point input) { |
|
|
|
|
|
|
|
|
|
if (input==null) { |
|
|
|
|
if (input == null) { |
|
|
|
|
input = new Point(); |
|
|
|
|
} |
|
|
|
|
input.setLocation(-1, -1); |
|
|
|
@ -419,25 +424,25 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
|
|
|
|
|
// Actually position just BEFORE caret.
|
|
|
|
|
int caretPosition = textArea.getCaretPosition() - 1; |
|
|
|
|
RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument(); |
|
|
|
|
RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument(); |
|
|
|
|
char bracket = 0; |
|
|
|
|
|
|
|
|
|
// If the caret was at offset 0, we can't check "to its left."
|
|
|
|
|
if (caretPosition>=0) { |
|
|
|
|
if (caretPosition >= 0) { |
|
|
|
|
bracket = doc.charAt(caretPosition); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Try to match a bracket "to the right" of the caret if one
|
|
|
|
|
// was not found on the left.
|
|
|
|
|
int index = BRACKETS.indexOf(bracket); |
|
|
|
|
if (index==-1 && caretPosition<doc.getLength()-1) { |
|
|
|
|
if (index == -1 && caretPosition < doc.getLength() - 1) { |
|
|
|
|
bracket = doc.charAt(++caretPosition); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// First, see if the char was a bracket (one of "{[()]}").
|
|
|
|
|
if (index==-1) { |
|
|
|
|
if (index == -1) { |
|
|
|
|
index = BRACKETS.indexOf(bracket); |
|
|
|
|
if (index==-1) { |
|
|
|
|
if (index == -1) { |
|
|
|
|
return input; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -455,14 +460,13 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
Token token = doc.getTokenListForLine(curLine); |
|
|
|
|
token = RSyntaxUtilities.getTokenAtOffset(token, caretPosition); |
|
|
|
|
// All brackets are always returned as "separators."
|
|
|
|
|
if (token.getType()!=Token.SEPARATOR) { |
|
|
|
|
if (Objects.requireNonNull(token).getType() != Token.SEPARATOR) { |
|
|
|
|
return input; |
|
|
|
|
} |
|
|
|
|
if (index<3) { // One of "{[("
|
|
|
|
|
if (index < 3) { // One of "{[("
|
|
|
|
|
goForward = true; |
|
|
|
|
bracketMatch = BRACKETS.charAt(index + 3); |
|
|
|
|
} |
|
|
|
|
else { // One of ")]}"
|
|
|
|
|
} else { // One of ")]}"
|
|
|
|
|
goForward = false; |
|
|
|
|
bracketMatch = BRACKETS.charAt(index - 3); |
|
|
|
|
} |
|
|
|
@ -479,33 +483,32 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
|
|
|
|
|
while (true) { |
|
|
|
|
|
|
|
|
|
doc.getText(start,end-start, charSegment); |
|
|
|
|
doc.getText(start, end - start, charSegment); |
|
|
|
|
int segOffset = charSegment.offset; |
|
|
|
|
|
|
|
|
|
for (int i=segOffset; i<segOffset+charSegment.count; i++) { |
|
|
|
|
for (int i = segOffset; i < segOffset + charSegment.count; i++) { |
|
|
|
|
|
|
|
|
|
char ch = charSegment.array[i]; |
|
|
|
|
|
|
|
|
|
if (ch==bracket) { |
|
|
|
|
if (haveTokenList==false) { |
|
|
|
|
if (ch == bracket) { |
|
|
|
|
if (!haveTokenList) { |
|
|
|
|
token = doc.getTokenListForLine(curLine); |
|
|
|
|
haveTokenList = true; |
|
|
|
|
} |
|
|
|
|
int offset = start + (i-segOffset); |
|
|
|
|
int offset = start + (i - segOffset); |
|
|
|
|
token = RSyntaxUtilities.getTokenAtOffset(token, offset); |
|
|
|
|
if (token.getType()==Token.SEPARATOR) |
|
|
|
|
if (Objects.requireNonNull(token).getType() == Token.SEPARATOR) { |
|
|
|
|
numEmbedded++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
else if (ch==bracketMatch) { |
|
|
|
|
if (haveTokenList==false) { |
|
|
|
|
} else if (ch == bracketMatch) { |
|
|
|
|
if (!haveTokenList) { |
|
|
|
|
token = doc.getTokenListForLine(curLine); |
|
|
|
|
haveTokenList = true; |
|
|
|
|
} |
|
|
|
|
int offset = start + (i-segOffset); |
|
|
|
|
int offset = start + (i - segOffset); |
|
|
|
|
token = RSyntaxUtilities.getTokenAtOffset(token, offset); |
|
|
|
|
if (token.getType()==Token.SEPARATOR) { |
|
|
|
|
if (numEmbedded==0) { |
|
|
|
|
if (Objects.requireNonNull(token).getType() == Token.SEPARATOR) { |
|
|
|
|
if (numEmbedded == 0) { |
|
|
|
|
if (textArea.isCodeFoldingEnabled() && |
|
|
|
|
textArea.getFoldManager().isLineHidden(curLine)) { |
|
|
|
|
return input; // Match hidden in a fold
|
|
|
|
@ -521,8 +524,9 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
|
|
|
|
|
// Bail out if we've gone through all lines and
|
|
|
|
|
// haven't found the match.
|
|
|
|
|
if (++curLine==lastLine) |
|
|
|
|
if (++curLine == lastLine) { |
|
|
|
|
return input; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Otherwise, go through the next line.
|
|
|
|
|
haveTokenList = false; |
|
|
|
@ -548,34 +552,33 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
|
|
|
|
|
while (true) { |
|
|
|
|
|
|
|
|
|
doc.getText(start,end-start, charSegment); |
|
|
|
|
doc.getText(start, end - start, charSegment); |
|
|
|
|
int segOffset = charSegment.offset; |
|
|
|
|
int iStart = segOffset + charSegment.count - 1; |
|
|
|
|
|
|
|
|
|
for (int i=iStart; i>=segOffset; i--) { |
|
|
|
|
for (int i = iStart; i >= segOffset; i--) { |
|
|
|
|
|
|
|
|
|
char ch = charSegment.array[i]; |
|
|
|
|
|
|
|
|
|
if (ch==bracket) { |
|
|
|
|
if (haveTokenList==false) { |
|
|
|
|
if (ch == bracket) { |
|
|
|
|
if (!haveTokenList) { |
|
|
|
|
token = doc.getTokenListForLine(curLine); |
|
|
|
|
haveTokenList = true; |
|
|
|
|
} |
|
|
|
|
int offset = start + (i-segOffset); |
|
|
|
|
int offset = start + (i - segOffset); |
|
|
|
|
t2 = RSyntaxUtilities.getTokenAtOffset(token, offset); |
|
|
|
|
if (t2.getType()==Token.SEPARATOR) |
|
|
|
|
if (Objects.requireNonNull(t2).getType() == Token.SEPARATOR) { |
|
|
|
|
numEmbedded++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
else if (ch==bracketMatch) { |
|
|
|
|
if (haveTokenList==false) { |
|
|
|
|
} else if (ch == bracketMatch) { |
|
|
|
|
if (!haveTokenList) { |
|
|
|
|
token = doc.getTokenListForLine(curLine); |
|
|
|
|
haveTokenList = true; |
|
|
|
|
} |
|
|
|
|
int offset = start + (i-segOffset); |
|
|
|
|
int offset = start + (i - segOffset); |
|
|
|
|
t2 = RSyntaxUtilities.getTokenAtOffset(token, offset); |
|
|
|
|
if (t2.getType()==Token.SEPARATOR) { |
|
|
|
|
if (numEmbedded==0) { |
|
|
|
|
if (Objects.requireNonNull(t2).getType() == Token.SEPARATOR) { |
|
|
|
|
if (numEmbedded == 0) { |
|
|
|
|
input.setLocation(caretPosition, offset); |
|
|
|
|
return input; |
|
|
|
|
} |
|
|
|
@ -587,7 +590,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
|
|
|
|
|
// Bail out if we've gone through all lines and
|
|
|
|
|
// haven't found the match.
|
|
|
|
|
if (--curLine==-1) { |
|
|
|
|
if (--curLine == -1) { |
|
|
|
|
return input; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -625,10 +628,10 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
*/ |
|
|
|
|
public static final Token getNextImportantToken(Token t, |
|
|
|
|
RSyntaxTextArea textArea, int line) { |
|
|
|
|
while (t!=null && t.isPaintable() && t.isCommentOrWhitespace()) { |
|
|
|
|
while (t != null && t.isPaintable() && t.isCommentOrWhitespace()) { |
|
|
|
|
t = t.getNextToken(); |
|
|
|
|
} |
|
|
|
|
if ((t==null || !t.isPaintable()) && line<textArea.getLineCount()-1) { |
|
|
|
|
if ((t == null || !t.isPaintable()) && line < textArea.getLineCount() - 1) { |
|
|
|
|
t = textArea.getTokenListForLine(++line); |
|
|
|
|
return getNextImportantToken(t, textArea, line); |
|
|
|
|
} |
|
|
|
@ -642,7 +645,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
* Some views may not be visible, |
|
|
|
|
* they might not be in the same order found in the model, or they just |
|
|
|
|
* might not allow access to some of the locations in the model.<p> |
|
|
|
|
* |
|
|
|
|
* <p> |
|
|
|
|
* NOTE: You should only call this method if the passed-in |
|
|
|
|
* <code>javax.swing.text.View</code> is an instance of |
|
|
|
|
* {@link TokenOrientedView} and <code>javax.swing.text.TabExpander</code>; |
|
|
|
@ -661,8 +664,8 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
* </ul> |
|
|
|
|
* @return the location within the model that best represents the next |
|
|
|
|
* location visual position |
|
|
|
|
* @exception BadLocationException |
|
|
|
|
* @exception IllegalArgumentException if <code>direction</code> |
|
|
|
|
* @throws BadLocationException |
|
|
|
|
* @throws IllegalArgumentException if <code>direction</code> |
|
|
|
|
* doesn't have one of the legal values above |
|
|
|
|
*/ |
|
|
|
|
public static int getNextVisualPositionFrom(int pos, Position.Bias b, |
|
|
|
@ -670,7 +673,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
Position.Bias[] biasRet, View view) |
|
|
|
|
throws BadLocationException { |
|
|
|
|
|
|
|
|
|
RSyntaxTextArea target = (RSyntaxTextArea)view.getContainer(); |
|
|
|
|
RSyntaxTextArea target = (RSyntaxTextArea) view.getContainer(); |
|
|
|
|
biasRet[0] = Position.Bias.Forward; |
|
|
|
|
|
|
|
|
|
// Do we want the "next position" above, below, to the left or right?
|
|
|
|
@ -688,37 +691,39 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
// YECK! Ideally, the x location from the magic caret
|
|
|
|
|
// position would be passed in.
|
|
|
|
|
Point mcp; |
|
|
|
|
if (c != null) |
|
|
|
|
if (c != null) { |
|
|
|
|
mcp = c.getMagicCaretPosition(); |
|
|
|
|
else |
|
|
|
|
} else { |
|
|
|
|
mcp = null; |
|
|
|
|
} |
|
|
|
|
int x; |
|
|
|
|
if (mcp == null) { |
|
|
|
|
Rectangle loc = target.modelToView(pos); |
|
|
|
|
Rectangle loc = Objects.requireNonNull(target).modelToView(pos); |
|
|
|
|
x = (loc == null) ? 0 : loc.x; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
x = mcp.x; |
|
|
|
|
} |
|
|
|
|
if (direction == NORTH) |
|
|
|
|
pos = getPositionAbove(target,pos,x,(TabExpander)view); |
|
|
|
|
else |
|
|
|
|
pos = getPositionBelow(target,pos,x,(TabExpander)view); |
|
|
|
|
if (direction == NORTH) { |
|
|
|
|
pos = getPositionAbove(target, pos, x, (TabExpander) view); |
|
|
|
|
} else { |
|
|
|
|
pos = getPositionBelow(target, pos, x, (TabExpander) view); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case WEST: |
|
|
|
|
if(pos == -1) { |
|
|
|
|
if (pos == -1) { |
|
|
|
|
pos = Math.max(0, view.getEndOffset() - 1); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
pos = Math.max(0, pos - 1); |
|
|
|
|
if (target.isCodeFoldingEnabled()) { |
|
|
|
|
int last = target.getLineOfOffset(pos+1); |
|
|
|
|
int last = target.getLineOfOffset(pos + 1); |
|
|
|
|
int current = target.getLineOfOffset(pos); |
|
|
|
|
if (last!=current) { // If moving up a line...
|
|
|
|
|
if (last != current) { // If moving up a line...
|
|
|
|
|
FoldManager fm = target.getFoldManager(); |
|
|
|
|
if (fm.isLineHidden(current)) { |
|
|
|
|
while (--current>0 && fm.isLineHidden(current)); |
|
|
|
|
while (--current > 0 && fm.isLineHidden(current)) { |
|
|
|
|
; |
|
|
|
|
} |
|
|
|
|
pos = target.getLineEndOffset(current) - 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -727,21 +732,22 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case EAST: |
|
|
|
|
if(pos == -1) { |
|
|
|
|
if (pos == -1) { |
|
|
|
|
pos = view.getStartOffset(); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
pos = Math.min(pos + 1, view.getDocument().getLength()); |
|
|
|
|
if (target.isCodeFoldingEnabled()) { |
|
|
|
|
int last = target.getLineOfOffset(pos-1); |
|
|
|
|
int last = target.getLineOfOffset(pos - 1); |
|
|
|
|
int current = target.getLineOfOffset(pos); |
|
|
|
|
if (last!=current) { // If moving down a line...
|
|
|
|
|
if (last != current) { // If moving down a line...
|
|
|
|
|
FoldManager fm = target.getFoldManager(); |
|
|
|
|
if (fm.isLineHidden(current)) { |
|
|
|
|
int lineCount = target.getLineCount(); |
|
|
|
|
while (++current<lineCount && fm.isLineHidden(current)); |
|
|
|
|
pos = current==lineCount ? |
|
|
|
|
target.getLineEndOffset(last)-1 : // Was the last visible line
|
|
|
|
|
while (++current < lineCount && fm.isLineHidden(current)) { |
|
|
|
|
; |
|
|
|
|
} |
|
|
|
|
pos = current == lineCount ? |
|
|
|
|
target.getLineEndOffset(last) - 1 : // Was the last visible line
|
|
|
|
|
target.getLineStartOffset(current); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -770,23 +776,22 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
* @param x the X coordinate >= 0 |
|
|
|
|
* @return the position >= 0 if the request can be computed, otherwise |
|
|
|
|
* a value of -1 will be returned. |
|
|
|
|
* @exception BadLocationException if the offset is out of range |
|
|
|
|
* @throws BadLocationException if the offset is out of range |
|
|
|
|
*/ |
|
|
|
|
public static final int getPositionAbove(RSyntaxTextArea c, int offs, |
|
|
|
|
float x, TabExpander e) throws BadLocationException { |
|
|
|
|
|
|
|
|
|
TokenOrientedView tov = (TokenOrientedView)e; |
|
|
|
|
TokenOrientedView tov = (TokenOrientedView) e; |
|
|
|
|
Token token = tov.getTokenListForPhysicalLineAbove(offs); |
|
|
|
|
if (token==null) |
|
|
|
|
if (token == null) { |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// A line containing only Token.NULL is an empty line.
|
|
|
|
|
else if (token.getType()==Token.NULL) { |
|
|
|
|
else if (token.getType() == Token.NULL) { |
|
|
|
|
int line = c.getLineOfOffset(offs); // Sure to be >0 ??
|
|
|
|
|
return c.getLineStartOffset(line-1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
else { |
|
|
|
|
return c.getLineStartOffset(line - 1); |
|
|
|
|
} else { |
|
|
|
|
return token.getListOffset(c, e, 0, x); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -804,26 +809,25 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
* @param x the X coordinate >= 0 |
|
|
|
|
* @return the position >= 0 if the request can be computed, otherwise |
|
|
|
|
* a value of -1 will be returned. |
|
|
|
|
* @exception BadLocationException if the offset is out of range |
|
|
|
|
* @throws BadLocationException if the offset is out of range |
|
|
|
|
*/ |
|
|
|
|
public static final int getPositionBelow(RSyntaxTextArea c, int offs, |
|
|
|
|
float x, TabExpander e) throws BadLocationException { |
|
|
|
|
|
|
|
|
|
TokenOrientedView tov = (TokenOrientedView)e; |
|
|
|
|
TokenOrientedView tov = (TokenOrientedView) e; |
|
|
|
|
Token token = tov.getTokenListForPhysicalLineBelow(offs); |
|
|
|
|
if (token==null) |
|
|
|
|
if (token == null) { |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// A line containing only Token.NULL is an empty line.
|
|
|
|
|
else if (token.getType()==Token.NULL) { |
|
|
|
|
else if (token.getType() == Token.NULL) { |
|
|
|
|
int line = c.getLineOfOffset(offs); // Sure to be > c.getLineCount()-1 ??
|
|
|
|
|
// return c.getLineStartOffset(line+1);
|
|
|
|
|
FoldManager fm = c.getFoldManager(); |
|
|
|
|
line = fm.getVisibleLineBelow(line); |
|
|
|
|
return c.getLineStartOffset(line); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
else { |
|
|
|
|
FoldManager fm = c.getFoldManager(); |
|
|
|
|
line = fm.getVisibleLineBelow(line); |
|
|
|
|
return c.getLineStartOffset(line); |
|
|
|
|
} else { |
|
|
|
|
return token.getListOffset(c, e, 0, x); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -841,18 +845,18 @@ return c.getLineStartOffset(line);
|
|
|
|
|
* @see #getNextImportantToken(Token, RSyntaxTextArea, int) |
|
|
|
|
*/ |
|
|
|
|
public static final Token getPreviousImportantToken( |
|
|
|
|
RSyntaxTextArea textArea, int line){ |
|
|
|
|
if (line<0) { |
|
|
|
|
RSyntaxTextArea textArea, int line) { |
|
|
|
|
if (line < 0) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
Token t = textArea.getTokenListForLine(line); |
|
|
|
|
if (t!=null) { |
|
|
|
|
if (t != null) { |
|
|
|
|
t = t.getLastNonCommentNonWhitespaceToken(); |
|
|
|
|
if (t!=null) { |
|
|
|
|
if (t != null) { |
|
|
|
|
return t; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return getPreviousImportantToken(textArea, line-1); |
|
|
|
|
return getPreviousImportantToken(textArea, line - 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -868,10 +872,11 @@ return c.getLineStartOffset(line);
|
|
|
|
|
* none of the tokens are at that offset. |
|
|
|
|
*/ |
|
|
|
|
public static final Token getTokenAtOffset(Token tokenList, int offset) { |
|
|
|
|
for (Token t=tokenList; t!=null && t.isPaintable(); t=t.getNextToken()){ |
|
|
|
|
if (t.containsPosition(offset)) |
|
|
|
|
for (Token t = tokenList; t != null && t.isPaintable(); t = t.getNextToken()) { |
|
|
|
|
if (t.containsPosition(offset)) { |
|
|
|
|
return t; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -895,18 +900,20 @@ return c.getLineStartOffset(line);
|
|
|
|
|
return offs; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
String s = doc.getText(offs, lineEnd-offs-1); |
|
|
|
|
if (s!=null && s.length()>0) { // Should always be true
|
|
|
|
|
String s = doc.getText(offs, lineEnd - offs - 1); |
|
|
|
|
if (s != null && s.length() > 0) { // Should always be true
|
|
|
|
|
int i = 0; |
|
|
|
|
int count = s.length(); |
|
|
|
|
char ch = s.charAt(i); |
|
|
|
|
if (Character.isWhitespace(ch)) { |
|
|
|
|
while (i<count && Character.isWhitespace(s.charAt(i++))); |
|
|
|
|
while (i < count && Character.isWhitespace(s.charAt(i++))) { |
|
|
|
|
; |
|
|
|
|
} |
|
|
|
|
else if (Character.isLetterOrDigit(ch)) { |
|
|
|
|
while (i<count && Character.isLetterOrDigit(s.charAt(i++))); |
|
|
|
|
} else if (Character.isLetterOrDigit(ch)) { |
|
|
|
|
while (i < count && Character.isLetterOrDigit(s.charAt(i++))) { |
|
|
|
|
; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
i = 2; |
|
|
|
|
} |
|
|
|
|
offs += i - 1; |
|
|
|
@ -935,23 +942,22 @@ return c.getLineStartOffset(line);
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int lineStart = line.getStartOffset(); |
|
|
|
|
if (offs==lineStart) { // Start of the line.
|
|
|
|
|
if (offs == lineStart) { // Start of the line.
|
|
|
|
|
return offs; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int endOffs = Math.min(offs+1, doc.getLength()); |
|
|
|
|
String s = doc.getText(lineStart, endOffs-lineStart); |
|
|
|
|
if(s != null && s.length() > 0) { |
|
|
|
|
int endOffs = Math.min(offs + 1, doc.getLength()); |
|
|
|
|
String s = doc.getText(lineStart, endOffs - lineStart); |
|
|
|
|
if (s != null && s.length() > 0) { |
|
|
|
|
int i = s.length() - 1; |
|
|
|
|
char ch = s.charAt(i); |
|
|
|
|
if (Character.isWhitespace(ch)) { |
|
|
|
|
while (i>0 && Character.isWhitespace(s.charAt(i-1))) { |
|
|
|
|
while (i > 0 && Character.isWhitespace(s.charAt(i - 1))) { |
|
|
|
|
i--; |
|
|
|
|
} |
|
|
|
|
offs = lineStart + i; |
|
|
|
|
} |
|
|
|
|
else if (Character.isLetterOrDigit(ch)) { |
|
|
|
|
while (i>0 && Character.isLetterOrDigit(s.charAt(i-1))) { |
|
|
|
|
} else if (Character.isLetterOrDigit(ch)) { |
|
|
|
|
while (i > 0 && Character.isLetterOrDigit(s.charAt(i - 1))) { |
|
|
|
|
i--; |
|
|
|
|
} |
|
|
|
|
offs = lineStart + i; |
|
|
|
@ -968,7 +974,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
* Determines the width of the given token list taking tabs |
|
|
|
|
* into consideration. This is implemented in a 1.1 style coordinate |
|
|
|
|
* system where ints are used and 72dpi is assumed.<p> |
|
|
|
|
* |
|
|
|
|
* <p> |
|
|
|
|
* This method also assumes that the passed-in token list begins at |
|
|
|
|
* x-pixel <code>0</code> in the view (for tab purposes). |
|
|
|
|
* |
|
|
|
@ -1000,7 +1006,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
RSyntaxTextArea textArea, |
|
|
|
|
TabExpander e, float x0) { |
|
|
|
|
float width = x0; |
|
|
|
|
for (Token t=tokenList; t!=null&&t.isPaintable(); t=t.getNextToken()) { |
|
|
|
|
for (Token t = tokenList; t != null && t.isPaintable(); t = t.getNextToken()) { |
|
|
|
|
width += t.getWidth(textArea, e, width); |
|
|
|
|
} |
|
|
|
|
return width - x0; |
|
|
|
@ -1029,12 +1035,12 @@ return c.getLineStartOffset(line);
|
|
|
|
|
RSyntaxTextArea textArea, TabExpander e, |
|
|
|
|
float x0, int upTo) { |
|
|
|
|
float width = 0; |
|
|
|
|
for (Token t=tokenList; t!=null&&t.isPaintable(); t=t.getNextToken()) { |
|
|
|
|
for (Token t = tokenList; t != null && t.isPaintable(); t = t.getNextToken()) { |
|
|
|
|
if (t.containsPosition(upTo)) { |
|
|
|
|
return width + t.getWidthUpTo(upTo-t.getOffset(), textArea, e, |
|
|
|
|
x0+width); |
|
|
|
|
return width + t.getWidthUpTo(upTo - t.getOffset(), textArea, e, |
|
|
|
|
x0 + width); |
|
|
|
|
} |
|
|
|
|
width += t.getWidth(textArea, e, x0+width); |
|
|
|
|
width += t.getWidth(textArea, e, x0 + width); |
|
|
|
|
} |
|
|
|
|
return width; |
|
|
|
|
} |
|
|
|
@ -1052,7 +1058,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
// We need the first condition as it might be that ch>255, and thus
|
|
|
|
|
// not in our table. '}' is the highest-valued char in the bracket
|
|
|
|
|
// set.
|
|
|
|
|
return ch<='}' && (dataTable[ch]&BRACKET_MASK)>0; |
|
|
|
|
return ch <= '}' && (dataTable[ch] & BRACKET_MASK) > 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1066,7 +1072,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
// We do it this way as we'd need to do two conditions anyway (first
|
|
|
|
|
// to check that ch<255 so it can index into our table, then whether
|
|
|
|
|
// that table position has the digit mask).
|
|
|
|
|
return ch>='0' && ch<='9'; |
|
|
|
|
return ch >= '0' && ch <= '9'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1082,7 +1088,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
// We need the first condition as it could be that ch>255 (and thus
|
|
|
|
|
// not a valid index into our table). 'f' is the highest-valued
|
|
|
|
|
// char that is a valid hex character.
|
|
|
|
|
return (ch<='f') && (dataTable[ch]&HEX_CHARACTER_MASK)>0; |
|
|
|
|
return (ch <= 'f') && (dataTable[ch] & HEX_CHARACTER_MASK) > 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1097,7 +1103,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
// We need the first condition as it could be that ch>255 (and thus
|
|
|
|
|
// not a valid index into our table). '~' is the highest-valued
|
|
|
|
|
// char that is a valid Java operator.
|
|
|
|
|
return (ch<='~') && (dataTable[ch]&JAVA_OPERATOR_MASK)>0; |
|
|
|
|
return (ch <= '~') && (dataTable[ch] & JAVA_OPERATOR_MASK) > 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1110,7 +1116,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
public static final boolean isLetter(char ch) { |
|
|
|
|
// We need the first condition as it could be that ch>255 (and thus
|
|
|
|
|
// not a valid index into our table).
|
|
|
|
|
return (ch<='z') && (dataTable[ch]&LETTER_MASK)>0; |
|
|
|
|
return (ch <= 'z') && (dataTable[ch] & LETTER_MASK) > 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1123,7 +1129,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
public static final boolean isLetterOrDigit(char ch) { |
|
|
|
|
// We need the first condition as it could be that ch>255 (and thus
|
|
|
|
|
// not a valid index into our table).
|
|
|
|
|
return (ch<='z') && (dataTable[ch]&LETTER_OR_DIGIT_MASK)>0; |
|
|
|
|
return (ch <= 'z') && (dataTable[ch] & LETTER_OR_DIGIT_MASK) > 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1137,7 +1143,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
* @see #getHyperlinkForeground() |
|
|
|
|
*/ |
|
|
|
|
public static final boolean isLightForeground(Color fg) { |
|
|
|
|
return fg.getRed()>0xa0 && fg.getGreen()>0xa0 && fg.getBlue()>0xa0; |
|
|
|
|
return fg.getRed() > 0xa0 && fg.getGreen() > 0xa0 && fg.getBlue() > 0xa0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1153,7 +1159,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
// We do it this way as we'd need to do two conditions anyway (first
|
|
|
|
|
// to check that ch<255 so it can index into our table, then whether
|
|
|
|
|
// that table position has the whitespace mask).
|
|
|
|
|
return ch==' ' || ch=='\t'; |
|
|
|
|
return ch == ' ' || ch == '\t'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1168,21 +1174,21 @@ return c.getLineStartOffset(line);
|
|
|
|
|
public static boolean regexCanFollowInJavaScript(Token t) { |
|
|
|
|
char ch; |
|
|
|
|
// We basically try to mimic Eclipse's JS editor's behavior here.
|
|
|
|
|
return t==null || |
|
|
|
|
return t == null || |
|
|
|
|
//t.isOperator() ||
|
|
|
|
|
(t.length()==1 && ( |
|
|
|
|
(ch=t.charAt(0))=='=' || |
|
|
|
|
ch=='(' || |
|
|
|
|
ch==',' || |
|
|
|
|
ch=='?' || |
|
|
|
|
ch==':' || |
|
|
|
|
ch=='[' || |
|
|
|
|
ch=='!' || |
|
|
|
|
ch=='&' |
|
|
|
|
(t.length() == 1 && ( |
|
|
|
|
(ch = t.charAt(0)) == '=' || |
|
|
|
|
ch == '(' || |
|
|
|
|
ch == ',' || |
|
|
|
|
ch == '?' || |
|
|
|
|
ch == ':' || |
|
|
|
|
ch == '[' || |
|
|
|
|
ch == '!' || |
|
|
|
|
ch == '&' |
|
|
|
|
)) || |
|
|
|
|
/* Operators "==", "===", "!=", "!==" */ |
|
|
|
|
(t.getType()==Token.OPERATOR && |
|
|
|
|
t.charAt(t.length()-1)=='=') || |
|
|
|
|
(t.getType() == Token.OPERATOR && |
|
|
|
|
t.charAt(t.length() - 1) == '=') || |
|
|
|
|
t.is(Token.RESERVED_WORD_2, JS_KEYWORD_RETURN); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1203,8 +1209,9 @@ return c.getLineStartOffset(line);
|
|
|
|
|
// We do it this way as we'd need to do two conditions anyway (first
|
|
|
|
|
// to check that ch<255 so it can index into our table, then whether
|
|
|
|
|
// that table position has the upper-case mask).
|
|
|
|
|
if (ch>='A' && ch<='Z') |
|
|
|
|
return (char)(ch | 0x20); |
|
|
|
|
if (ch >= 'A' && ch <= 'Z') { |
|
|
|
|
return (char) (ch | 0x20); |
|
|
|
|
} |
|
|
|
|
return ch; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1231,17 +1238,18 @@ return c.getLineStartOffset(line);
|
|
|
|
|
private static final int getOSImpl() { |
|
|
|
|
int os = OS_OTHER; |
|
|
|
|
String osName = System.getProperty("os.name"); |
|
|
|
|
if (osName!=null) { // Should always be true.
|
|
|
|
|
if (osName != null) { // Should always be true.
|
|
|
|
|
osName = osName.toLowerCase(); |
|
|
|
|
if (osName.indexOf("windows") > -1) |
|
|
|
|
if (osName.contains("windows")) { |
|
|
|
|
os = OS_WINDOWS; |
|
|
|
|
else if (osName.indexOf("mac os x") > -1) |
|
|
|
|
} else if (osName.contains("mac os x")) { |
|
|
|
|
os = OS_MAC_OSX; |
|
|
|
|
else if (osName.indexOf("linux") > -1) |
|
|
|
|
} else if (osName.contains("linux")) { |
|
|
|
|
os = OS_LINUX; |
|
|
|
|
else |
|
|
|
|
} else { |
|
|
|
|
os = OS_OTHER; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return os; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1260,11 +1268,11 @@ return c.getLineStartOffset(line);
|
|
|
|
|
|
|
|
|
|
int flags = 0; |
|
|
|
|
if (!matchCase) { |
|
|
|
|
flags = Pattern.CASE_INSENSITIVE|Pattern.UNICODE_CASE; |
|
|
|
|
flags = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
StringBuilder sb = new StringBuilder(); |
|
|
|
|
for (int i=0; i<wildcard.length(); i++) { |
|
|
|
|
for (int i = 0; i < wildcard.length(); i++) { |
|
|
|
|
char ch = wildcard.charAt(i); |
|
|
|
|
switch (ch) { |
|
|
|
|
case '*': |
|
|
|
@ -1274,18 +1282,23 @@ return c.getLineStartOffset(line);
|
|
|
|
|
sb.append('.'); |
|
|
|
|
break; |
|
|
|
|
case '^': |
|
|
|
|
if (i>0 || escapeStartChar) { |
|
|
|
|
if (i > 0 || escapeStartChar) { |
|
|
|
|
sb.append('\\'); |
|
|
|
|
} |
|
|
|
|
sb.append('^'); |
|
|
|
|
break; |
|
|
|
|
case '\\': |
|
|
|
|
case '.': case '|': |
|
|
|
|
case '+': case '-': |
|
|
|
|
case '.': |
|
|
|
|
case '|': |
|
|
|
|
case '+': |
|
|
|
|
case '-': |
|
|
|
|
case '$': |
|
|
|
|
case '[': case ']': |
|
|
|
|
case '{': case '}': |
|
|
|
|
case '(': case ')': |
|
|
|
|
case '[': |
|
|
|
|
case ']': |
|
|
|
|
case '{': |
|
|
|
|
case '}': |
|
|
|
|
case '(': |
|
|
|
|
case ')': |
|
|
|
|
sb.append('\\').append(ch); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|