未验证 提交 9b896168 编写于 作者: E Elizabeth 提交者: GitHub

#13756 Add more blocks to autoedit (#15955)

* #13756 add more blocks for autoclose

* #13756 add closed blocks recognition and refactor

* #13756 move information about block completions to dialect

* #13756 refactor
上级 d55c033f
......@@ -58,6 +58,15 @@ public abstract class AbstractSQLDialect implements SQLDialect {
public static final String[] DML_KEYWORDS = new String[0];
public static final Pair<String, String> IN_CLAUSE_PARENTHESES = new Pair<>("(", ")");
protected static final SQLBlockCompletions DEFAULT_SQL_BLOCK_COMPLETIONS = new SQLBlockCompletionsCollection() {{
registerCompletionPair("BEGIN", "END");
registerCompletionPair("CASE", "END");
registerCompletionPair("LOOP", "END", "LOOP");
registerCompletionInfo("IF", new String[] { " THEN", SQLBlockCompletions.NEW_LINE_COMPLETION_PART,
SQLBlockCompletions.ONE_INDENT_COMPLETION_PART, SQLBlockCompletions.NEW_LINE_COMPLETION_PART, "END IF", SQLBlockCompletions.NEW_LINE_COMPLETION_PART
}, "END", "IF");
}};
// Keywords
private TreeMap<String, DBPKeywordType> allKeywords = new TreeMap<>();
......@@ -862,6 +871,11 @@ public abstract class AbstractSQLDialect implements SQLDialect {
public boolean hasCaseSensitiveFiltration() {
return false;
}
@Override
public SQLBlockCompletions getBlockCompletions() {
return DEFAULT_SQL_BLOCK_COMPLETIONS;
}
}
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jkiss.dbeaver.model.sql;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.utils.CommonUtils;
public class SQLBlockCompletionInfo {
private final SQLBlockCompletionsCollection owner;
private final int headTokenId;
private final String[] completionParts;
private final int tailTokenId;
private final Integer tailEndTokenId;
private final Integer headCancelTokenId;
/**
* @param owner - SQLBlockCompletionsCollection where SQLBlockCompletionInfo is registered
* @param headTokenId - id of the beginning token of the block.
* @param completionParts - array of strings which should be inserted on autoedit
* Completion part can be a String, SQLBlockCompletions.ONE_INDENT_COMPLETION_PART - indentation, SQLBlockCompletions.NEW_LINE_COMPLETION_PART - new line.
* @param tailTokenId - id of the first token of the block end
* @param tailEndTokenId - id of the last token of the block end
* @param prevCancelTokenId - token that shouldn't precede the block begin token
*/
public SQLBlockCompletionInfo(@NotNull SQLBlockCompletionsCollection owner, int headTokenId, @Nullable String[] completionParts,
int tailTokenId, @Nullable Integer tailEndTokenId, @Nullable Integer prevCancelTokenId) {
this.owner = owner;
this.headTokenId = headTokenId;
this.completionParts = completionParts;
this.tailTokenId = tailTokenId;
this.tailEndTokenId = tailEndTokenId;
this.headCancelTokenId = prevCancelTokenId;
}
public int getHeadTokenId() {
return headTokenId;
}
@Nullable
public String[] getCompletionParts() {
return completionParts;
}
public int getTailTokenId() {
return tailTokenId;
}
@Nullable
public Integer getTailEndTokenId() {
return tailEndTokenId;
}
@Nullable
public Integer getHeadCancelTokenId() {
return headCancelTokenId;
}
@NotNull
private String getTokenString(Integer tokenId) {
return tokenId == null ? "<UNBOUND>" : CommonUtils.notNull(owner.findTokenString((int)tokenId), "<UNKNOWN TOKEN ID #" + tokenId + ">");
}
@Override
@NotNull
public String toString() {
return (headCancelTokenId == null ? "" : ("[! " + getTokenString(headCancelTokenId) + "]")) +
getTokenString(headTokenId) + " ... " + getTokenString(tailTokenId) + " " + getTokenString(tailEndTokenId);
}
}
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jkiss.dbeaver.model.sql;
import org.jkiss.code.Nullable;
public interface SQLBlockCompletions {
String ONE_INDENT_COMPLETION_PART = "\t";
String NEW_LINE_COMPLETION_PART = null;
int KNOWN_TOKEN_ID_BASE = 100;
@Nullable
Integer findTokenId(String str);
@Nullable
SQLBlockCompletionInfo findCompletionByHead(int headTokenId);
}
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jkiss.dbeaver.model.sql;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Predicate;
import java.util.regex.Pattern;
/**
* A set of information about blocks for autoedit strategy.
*/
public class SQLBlockCompletionsCollection implements SQLBlockCompletions {
private static final Predicate<String> RECOGNIZABLE_TOKEN_PATTERN = Pattern.compile("^\\w+$").asMatchPredicate();
private final Map<String, Integer> tokenIdByString = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
private final List<String> tokenStringById = new ArrayList<>();
private final Map<Integer, SQLBlockCompletionInfo> blockCompletionByHeadToken = new HashMap<>();
private final Map<Integer, Map<Integer, Set<SQLBlockCompletionInfo>>> blockCompletionByTailToken = new HashMap<>();
/**
* Get SQLBlockCompletionInfo by block beginning token id.
*/
@Nullable
public SQLBlockCompletionInfo findCompletionByHead(int headTokenId) {
return blockCompletionByHeadToken.get(headTokenId);
}
@Nullable
public String findTokenString(int id) {
return tokenStringById.get(id - KNOWN_TOKEN_ID_BASE);
}
@Nullable
public Integer findTokenId(@NotNull String str) {
return tokenIdByString.get(str);
}
/**
* Get token id for token string.
* If token has't been registered yet, id will be generated.
*/
private int obtainTokenId(@NotNull String str) {
if (!RECOGNIZABLE_TOKEN_PATTERN.test(str)) {
throw new IllegalArgumentException("Illegal block completion part '" + str + "' while expecting keyword-like token.");
}
Integer id = tokenIdByString.get(str);
if (id == null) {
id = tokenStringById.size() + KNOWN_TOKEN_ID_BASE;
tokenStringById.add(str);
tokenIdByString.put(str, id);
}
return id;
}
/**
* Register block for autoedit containing token at the begin and token at the end (e.g. BEGIN .. END).
* @param headToken is a beginning token of the block
* @param tailToken is an ending token of the block
*/
public void registerCompletionPair(@NotNull String headToken, @NotNull String tailToken) {
this.registerBlockCompletionInfo(headToken, new String[] {
NEW_LINE_COMPLETION_PART, ONE_INDENT_COMPLETION_PART, NEW_LINE_COMPLETION_PART, tailToken, NEW_LINE_COMPLETION_PART
}, tailToken, null, null);
}
/**
* Register block for autoedit containing token at the begin and two tokens at the end (e.g. LOOP .. END LOOP).
* @param headToken is a beginning token of the block.
* @param tailToken is a beginning token of the block end.
* @param tailEndToken - last token of the block.
*/
public void registerCompletionPair(@NotNull String headToken, @NotNull String tailToken, @NotNull String tailEndToken) {
this.registerCompletionInfo(headToken, new String[] {
NEW_LINE_COMPLETION_PART, tailToken + " " + tailEndToken, NEW_LINE_COMPLETION_PART
}, tailToken, tailEndToken);
}
/**
* Register block for autoedit containing token at the begin, middle token and one or two tokens at the end (e.g. IF .. THEN .. END IF).
* @param headToken is a beginning token of the block.
* @param completionParts is an array of strings which should be inserted on autoedit.
* Completion part can be a String,
* SQLBlockCompletions.ONE_INDENT_COMPLETION_PART - indentation,
* SQLBlockCompletions.NEW_LINE_COMPLETION_PART - new line.
* @param tailToken - first token of the block end.
* @param tailEndToken - last token of the block end.
*/
public void registerCompletionInfo(@NotNull String headToken, @NotNull String[] completionParts,
@NotNull String tailToken, @Nullable String tailEndToken) {
this.registerBlockCompletionInfo(headToken, completionParts, tailToken, tailEndToken, headToken.equalsIgnoreCase(tailEndToken) ? tailToken : null);
}
private void registerBlockCompletionInfo(@NotNull String headToken, @NotNull String[] completionParts,
@NotNull String tailToken, @Nullable String tailEndToken, @Nullable String prevCancelToken) {
if (headToken == null || completionParts == null || tailToken == null) {
throw new IllegalArgumentException("Illegal block completion info. headToken, completionParts and tailToken are mandatory.");
}
SQLBlockCompletionInfo info = new SQLBlockCompletionInfo(
this,
obtainTokenId(headToken),
completionParts,
obtainTokenId(tailToken),
tailEndToken == null ? null : obtainTokenId(tailEndToken),
// token that shouldn't precede the block begin token (example: END for block LOOP .. END LOOP)
prevCancelToken == null ? null : obtainTokenId(prevCancelToken)
);
this.blockCompletionByHeadToken.put(info.getHeadTokenId(), info);
this.blockCompletionByTailToken.computeIfAbsent(info.getTailTokenId(), n -> new HashMap<>())
.computeIfAbsent(info.getTailEndTokenId(), n -> new HashSet<>())
.add(info);
}
}
......@@ -451,4 +451,9 @@ public interface SQLDialect {
default SQLTokenPredicateSet getSkipTokenPredicates() {
return EmptyTokenPredicateSet.INSTANCE;
}
/**
* @return a set of SQLBlockCompletions with information about blocks for autoedit
*/
SQLBlockCompletions getBlockCompletions();
}
......@@ -93,7 +93,7 @@ public class SQLPreferenceConstants
public static final String SQLEDITOR_CLOSE_DOUBLE_QUOTES = "SQLEditor.closeDoubleQuotes";
public static final String SQLEDITOR_CLOSE_BRACKETS = "SQLEditor.closeBrackets";
public static final String SQLEDITOR_CLOSE_COMMENTS = "SQLEditor.closeComments";
public static final String SQLEDITOR_CLOSE_BEGIN_END = "SQLEditor.closeBeginEndStatement";
public static final String SQLEDITOR_CLOSE_BLOCKS = "SQLEditor.closeBlocks";
// Matching brackets
public final static String MATCHING_BRACKETS = "SQLEditor.matchingBrackets";
......
......@@ -22,6 +22,8 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPKeywordType;
import org.jkiss.dbeaver.model.DBPMessageType;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
import org.jkiss.dbeaver.model.sql.SQLBlockCompletionInfo;
import org.jkiss.dbeaver.model.sql.SQLBlockCompletions;
import org.jkiss.dbeaver.model.sql.SQLConstants;
import org.jkiss.dbeaver.model.sql.SQLSyntaxManager;
import org.jkiss.dbeaver.model.sql.SQLUtils;
......@@ -31,19 +33,20 @@ import org.jkiss.dbeaver.runtime.DBeaverNotifications;
import org.jkiss.dbeaver.ui.editors.sql.SQLPreferenceConstants;
import org.jkiss.dbeaver.utils.GeneralUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
public class SQLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
private static final Log log = Log.getLog(SQLAutoIndentStrategy.class);
private static final int MINIMUM_SOUCE_CODE_LENGTH = 10;
private static final boolean KEYWORD_INDENT_ENABLED = false;
private final String oneIndent = SQLIndenter.createIndent().toString();
private String partitioning;
private ISourceViewer sourceViewer;
private SQLSyntaxManager syntaxManager;
private Map<Integer, String> autoCompletionMap = new HashMap<>();
private String[] delimiters;
private enum CommentType {
......@@ -55,8 +58,7 @@ public class SQLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
/**
* Creates a new SQL auto indent strategy for the given document partitioning.
*/
public SQLAutoIndentStrategy(String partitioning, ISourceViewer sourceViewer, SQLSyntaxManager syntaxManager)
{
public SQLAutoIndentStrategy(String partitioning, ISourceViewer sourceViewer, SQLSyntaxManager syntaxManager) {
this.partitioning = partitioning;
this.sourceViewer = sourceViewer;
this.syntaxManager = syntaxManager;
......@@ -304,10 +306,7 @@ public class SQLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
return false;
}
private void smartIndentAfterNewLine(IDocument document, DocumentCommand command)
{
clearCachedValues();
private void smartIndentAfterNewLine(IDocument document, DocumentCommand command) {
int docLength = document.getLength();
if (docLength == 0) {
return;
......@@ -318,18 +317,25 @@ public class SQLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
//get previous token
int previousToken = scanner.previousToken(command.offset - 1, SQLHeuristicScanner.UNBOUND);
int previousTokenPos = scanner.getPosition();
String lastTokenString = scanner.getLastToken();
int nextToken = scanner.nextToken(command.offset, SQLHeuristicScanner.UNBOUND);
SQLBlockCompletionInfo completion = isBlocksCompletionEnabled() ? syntaxManager.getDialect().getBlockCompletions().findCompletionByHead(previousToken) : null;
int prevPreviousToken = completion == null || completion.getHeadCancelTokenId() == null ?
SQLHeuristicScanner.NOT_FOUND : scanner.previousToken(previousTokenPos, SQLHeuristicScanner.UNBOUND);
boolean autoCompletionSupported = completion != null && (
completion.getHeadCancelTokenId() == null || ((int)completion.getHeadCancelTokenId()) != prevPreviousToken
);
String indent;
String beginIndentaion = "";
if (isSupportedAutoCompletionToken(previousToken)) {
if (autoCompletionSupported) {
indent = indenter.computeIndentation(command.offset);
beginIndentaion = indenter.getReferenceIndentation(command.offset);
} else if (nextToken == SQLIndentSymbols.Tokenend || nextToken == SQLIndentSymbols.TokenEND) {
indent = indenter.getReferenceIndentation(command.offset + 1);
// } else if (nextToken == SQLIndentSymbols.TokenEND) {
// indent = indenter.getReferenceIndentation(command.offset + 1);
} else if (KEYWORD_INDENT_ENABLED) {
if (previousToken == SQLIndentSymbols.TokenKeyword) {
int nlIndent = syntaxManager.getDialect().getKeywordNextLineIndent(lastTokenString);
......@@ -355,7 +361,7 @@ public class SQLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
// Keep current indent
} else {
// Last token seems to be some identifier (table or column or function name)
// Next line shoudl contain some keyword then - let's unindent
// Next line should contain some keyword then - let's unindent
indent = indenter.unindent(indent, 1);
// Do not unindent (#5753)
}
......@@ -388,24 +394,44 @@ public class SQLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
start = document.getLineInformationOfOffset(region.getOffset()).getOffset();
}
command.caretOffset = command.offset + buf.length();
command.shiftsCaret = false;
if (isSupportedAutoCompletionToken(previousToken) && !isClosed(document, command.offset, previousToken) && getTokenCount(start, command.offset, scanner, previousToken) > 0) {
buf.append(getLineDelimiter(document));
buf.append(beginIndentaion);
buf.append(getAutoCompletionTrail(previousToken));
if (autoCompletionSupported && getBlockBalance(document, command.offset, completion) > 0 && getTokenCount(start, command.offset, scanner, previousToken) > 0) {
buf.setLength(0);
for (String part: completion.getCompletionParts()) {
if (part == SQLBlockCompletions.NEW_LINE_COMPLETION_PART) {
buf.append(getLineDelimiter(document));
buf.append(beginIndentaion);
} else if (part.equals(SQLBlockCompletions.ONE_INDENT_COMPLETION_PART)) {
buf.append(oneIndent);
} else {
buf.append(adjustCase(lastTokenString, part));
}
}
command.caretOffset = command.offset;
} else {
command.caretOffset = command.offset + buf.length();
}
command.shiftsCaret = false;
command.text = buf.toString();
}
catch (BadLocationException e) {
} catch (BadLocationException e) {
log.error(e);
}
}
private static String adjustCase(String example, String value) {
return isLowerCase(example) ? value.toLowerCase() : value.toUpperCase();
}
private static boolean isLowerCase(String value) {
for (int i = 0, l = value.length(); i < l; i++) {
if (Character.isUpperCase(value.charAt(i))) {
return false;
}
}
return true;
}
private static String getLineDelimiter(IDocument document)
{
private static String getLineDelimiter(IDocument document) {
try {
if (document.getNumberOfLines() > 1) {
return document.getLineDelimiter(0);
......@@ -418,37 +444,17 @@ public class SQLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
return GeneralUtils.getDefaultLineSeparator();
}
private boolean isLineDelimiter(IDocument document, String text)
{
private boolean isLineDelimiter(IDocument document, String text) {
if (delimiters == null) {
delimiters = document.getLegalLineDelimiters();
}
return delimiters != null && TextUtilities.equals(delimiters, text) > -1;
}
private void clearCachedValues()
{
autoCompletionMap.clear();
DBPPreferenceStore preferenceStore = DBWorkbench.getPlatform().getPreferenceStore();
boolean closeBeginEnd = preferenceStore.getBoolean(SQLPreferenceConstants.SQLEDITOR_CLOSE_BEGIN_END);
if (closeBeginEnd) {
autoCompletionMap.put(SQLIndentSymbols.Tokenbegin, SQLIndentSymbols.end);
autoCompletionMap.put(SQLIndentSymbols.TokenBEGIN, SQLIndentSymbols.END);
}
}
private boolean isSupportedAutoCompletionToken(int token)
{
return autoCompletionMap.containsKey(token);
}
private String getAutoCompletionTrail(int token)
{
return autoCompletionMap.get(token);
private boolean isBlocksCompletionEnabled() {
return DBWorkbench.getPlatform().getPreferenceStore().getBoolean(SQLPreferenceConstants.SQLEDITOR_CLOSE_BLOCKS);
}
/**
* To count token numbers from start offset to end offset.
*/
......@@ -460,7 +466,7 @@ public class SQLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
while (startOffset < endOffset) {
int nextToken = scanner.nextToken(startOffset, endOffset);
int position = scanner.getPosition();
if (nextToken != SQLIndentSymbols.TokenEOF && scanner.isSameToken(nextToken, token)) {
if (nextToken != SQLIndentSymbols.TokenEOF && nextToken == token) {
tokenCount++;
}
startOffset = position;
......@@ -468,21 +474,11 @@ public class SQLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
return tokenCount;
}
private boolean isClosed(IDocument document, int offset, int token)
{
//currently only BEGIN/END is supported. Later more typing aids will be added here.
if (token == SQLIndentSymbols.TokenBEGIN || token == SQLIndentSymbols.Tokenbegin) {
return getBlockBalance(document, offset) <= 0;
}
return false;
}
/**
* Returns the block balance, i.e. zero if the blocks are balanced at <code>offset</code>, a negative number if
* there are more closing than opening peers, and a positive number if there are more opening than closing peers.
*/
private int getBlockBalance(IDocument document, int offset)
{
private int getBlockBalance(IDocument document, int offset, SQLBlockCompletionInfo blockInfo) {
if (offset < 1) {
return -1;
}
......@@ -496,9 +492,8 @@ public class SQLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
SQLHeuristicScanner scanner = new SQLHeuristicScanner(document, syntaxManager);
while (true) {
begin = scanner.findOpeningPeer(begin, SQLIndentSymbols.TokenBEGIN, SQLIndentSymbols.TokenEND);
end = scanner.findClosingPeer(end, SQLIndentSymbols.TokenBEGIN, SQLIndentSymbols.TokenEND);
begin = scanner.findOpeningPeer(begin, blockInfo);
end = scanner.findClosingPeer(end, blockInfo);
if (begin == -1 && end == -1) {
return 0;
}
......@@ -511,6 +506,5 @@ public class SQLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy {
}
}
}
......@@ -21,6 +21,7 @@ import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TextUtilities;
import org.jkiss.dbeaver.model.DBPKeywordType;
import org.jkiss.dbeaver.model.sql.SQLBlockCompletionInfo;
import org.jkiss.dbeaver.model.sql.SQLSyntaxManager;
import org.jkiss.dbeaver.model.sql.parser.SQLParserPartitions;
......@@ -262,25 +263,11 @@ public class SQLHeuristicScanner implements SQLIndentSymbols {
private int getToken(String s) {
assert (s != null);
switch (s.length()) {
case 3:
if (SQLIndentSymbols.end.equals(s)) {
return Tokenend;
}
if (SQLIndentSymbols.END.equalsIgnoreCase(s)) {
return TokenEND;
}
break;
case 5:
if (SQLIndentSymbols.begin.equals(s)) {
return Tokenbegin;
}
if (SQLIndentSymbols.BEGIN.equalsIgnoreCase(s)) {
return TokenBEGIN;
}
break;
Integer tokenKindId = this.syntaxManager.getDialect().getBlockCompletions().findTokenId(s);
if (tokenKindId != null) {
return tokenKindId;
}
final DBPKeywordType keywordType = syntaxManager.getDialect().getKeywordType(s);
if (keywordType == DBPKeywordType.KEYWORD) {
return TokenKeyword;
......@@ -420,27 +407,31 @@ public class SQLHeuristicScanner implements SQLIndentSymbols {
* Note that <code>start</code> must not point to the closing peer, but to the first token being searched.
* </p>
*
* @param start the start position
* @param openingPeer the opening peer token (e.g. 'begin')
* @param closingPeer the closing peer token (e.g. 'end')
* @param start the start position
* @param blockInfo information about completion block
* @return the matching peer character position, or <code>NOT_FOUND</code>
*/
public int findOpeningPeer(int start, int openingPeer, int closingPeer) {
public int findOpeningPeer(int start, SQLBlockCompletionInfo blockInfo) {
assert (start < document.getLength());
int openingPeer = blockInfo.getHeadTokenId();
int closingPeer = blockInfo.getTailTokenId();
int closingPeerEnd = blockInfo.getTailEndTokenId() != null ? blockInfo.getTailEndTokenId() : UNBOUND;
int headCancelToken = blockInfo.getHeadCancelTokenId() != null ? blockInfo.getHeadCancelTokenId() : UNBOUND;
int depth = 1;
start += 1;
int token;
int nextToken = NOT_FOUND;
int offset = start;
while (true) {
int nextTokenOffset = NOT_FOUND;
do {
token = previousToken(offset, UNBOUND);
offset = getPosition();
if (token == SQLIndentSymbols.TokenEOF) {
return NOT_FOUND;
}
if (isSameToken(token, closingPeer)) {
if (token == closingPeer && (closingPeerEnd == UNBOUND || nextToken == closingPeerEnd)) {
depth++;
} else if (isSameToken(token, openingPeer)) {
} else if ((headCancelToken == UNBOUND && token == openingPeer) || (headCancelToken != UNBOUND && (
(headCancelToken != token && nextToken == openingPeer)
))) {
depth--;
}
......@@ -448,10 +439,12 @@ public class SQLHeuristicScanner implements SQLIndentSymbols {
if (offset == -1) {
return 0;
}
return offset;
return headCancelToken == UNBOUND ? offset : nextTokenOffset;
}
}
nextToken = token;
nextTokenOffset = offset;
} while (token != SQLIndentSymbols.TokenEOF);
return NOT_FOUND;
}
/**
......@@ -462,19 +455,22 @@ public class SQLHeuristicScanner implements SQLIndentSymbols {
* token being searched.</p>
*
* @param start the start position
* @param openingPeer the opening peer character (e.g. 'begin')
* @param closingPeer the closing peer character (e.g. 'end')
* @param blockInfo information about opening peer character (e.g. 'begin'), closing peer character (e.g. 'end') and
* @return the matching peer character position, or <code>NOT_FOUND</code>
*/
public int findClosingPeer(int start, int openingPeer, int closingPeer) {
public int findClosingPeer(int start, SQLBlockCompletionInfo blockInfo) {
assert (start <= document.getLength());
int openingPeer = blockInfo.getHeadTokenId();
int closingPeer = blockInfo.getTailTokenId();
int closingPeerEnd = blockInfo.getTailEndTokenId() != null ? blockInfo.getTailEndTokenId() : UNBOUND;
int headCancelToken = blockInfo.getHeadCancelTokenId() != null ? blockInfo.getHeadCancelTokenId() : UNBOUND;
int depth = 1;
start += 1;
int token;
int prevToken = NOT_FOUND;
int offset = start;
while (true) {
token = nextToken(offset, document.getLength());
offset = getPosition();
......@@ -482,25 +478,18 @@ public class SQLHeuristicScanner implements SQLIndentSymbols {
return NOT_FOUND;
}
if (isSameToken(token, openingPeer)) {
if (token == openingPeer && prevToken != headCancelToken) {
depth++;
} else if (isSameToken(token, closingPeer)) {
} else if ((closingPeerEnd == UNBOUND && token == closingPeer) || (prevToken == closingPeer && token == closingPeerEnd)) {
depth--;
}
if (depth == 0) {
return offset;
}
prevToken = token;
}
}
public boolean isSameToken(int firstToken, int secondToken) {
return firstToken == secondToken ||
(firstToken == TokenBEGIN && secondToken == Tokenbegin) ||
(firstToken == Tokenbegin && secondToken == TokenBEGIN) ||
(firstToken == TokenEND && secondToken == Tokenend) ||
(firstToken == Tokenend && secondToken == TokenEND);
}
}
......@@ -17,24 +17,18 @@
package org.jkiss.dbeaver.ui.editors.sql.indent;
public interface SQLIndentSymbols
{
public interface SQLIndentSymbols {
/**
* remember to keep all these ids lower than <code>SQLBlockCompletions.KNOWN_TOKEN_ID_BASE</code>
*/
int TokenEOF = -1;
int TokenOTHER = 0;
int Tokenbegin = 1000;
int TokenBEGIN = 1001;
int Tokenend = 1002;
int TokenEND = 1003;
int TokenIDENT = 2000;
int TokenKeyword = 3000;
int TokenKeywordStart = 3001;
String BEGIN = "BEGIN";
String begin = "begin";
String end = "end";
String END = "END";
// String end2 = "end ";
// String END2 = "END ";
int TokenIDENT = 20;
int TokenKeyword = 30;
int TokenKeywordStart = 31;
}
......@@ -197,7 +197,7 @@ public class SQLIndenter {
*
* @return one indentation
*/
private StringBuilder createIndent() {
public static StringBuilder createIndent() {
IPreferenceStore preferenceStore = EditorsPlugin.getDefault().getPreferenceStore();
boolean useSpaces = preferenceStore.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS);
StringBuilder oneIndent = new StringBuilder();
......
......@@ -96,7 +96,7 @@ public class SQLEditorPreferencesInitializer extends AbstractPreferenceInitializ
PrefUtils.setDefaultPreferenceValue(store, SQLPreferenceConstants.SQLEDITOR_CLOSE_DOUBLE_QUOTES, true);
PrefUtils.setDefaultPreferenceValue(store, SQLPreferenceConstants.SQLEDITOR_CLOSE_BRACKETS, true);
PrefUtils.setDefaultPreferenceValue(store, SQLPreferenceConstants.SQLEDITOR_CLOSE_COMMENTS, true);
PrefUtils.setDefaultPreferenceValue(store, SQLPreferenceConstants.SQLEDITOR_CLOSE_BEGIN_END, true);
PrefUtils.setDefaultPreferenceValue(store, SQLPreferenceConstants.SQLEDITOR_CLOSE_BLOCKS, true);
PrefUtils.setDefaultPreferenceValue(store, SQLPreferenceConstants.SQL_FORMAT_KEYWORD_CASE_AUTO, true);
PrefUtils.setDefaultPreferenceValue(store, SQLPreferenceConstants.SQL_FORMAT_EXTRACT_FROM_SOURCE, true);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册