Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

优化建议 #165

Open
openai0229 opened this issue Dec 9, 2024 · 4 comments
Open

优化建议 #165

openai0229 opened this issue Dec 9, 2024 · 4 comments

Comments

@openai0229
Copy link

image

我觉得这种情况其实不应该提示,处于单行注释中,所以我觉得其实可以在调用C3之前先判断当前token的位置以及这个token的类型,像这个情况,就可以判断这个token处于单行注释中,就可以直接返回,不需要提示了,这个我已经在自己的demo中实现了,但是我用的是java语言写的,如果需要的话,我也可以把代码贴上来

@liuxy0551
Copy link
Collaborator

欢迎提供参考,谢谢

@openai0229
Copy link
Author

我写的比较简单,就自个写着玩玩的,还没工程化,可能有些bug。我就把代码都给贴出来吧

public class CursorTokenUtil {
    /**
     * 根据光标位置查找对应的令牌信息。
     *
     * @param cursorPosition 光标的位置,包括行号和列号,通常表示为文件中的绝对偏移位置。
     * @param tokenStream    所有令牌的列表,假设已按行号和列号(或字符索引)排序。
     * @return 包含光标位置的上下文 Token 信息的 {@link CursorTokenInfo} 对象。如果未找到合适的令牌,返回默认值。
     */
    public static CursorTokenInfo findCursorTokenInfo(CursorPosition cursorPosition, CommonTokenStream tokenStream) {
        // 如果令牌流为空或没有任何令牌,直接返回未找到的结果。
        if (tokenStream == null || tokenStream.size() == 0) {
            return CursorTokenInfo.noFindCursorToken();
        }

        // 获取光标位置的绝对偏移值。
        int currentPosition = cursorPosition.getCurrentPosition();

        // 如果光标位置超出了令牌流的范围(小于第一个令牌或大于最后一个令牌的范围),直接返回未找到结果。
        if (currentPosition < tokenStream.get(0).getStartIndex() ||
                currentPosition > tokenStream.get(tokenStream.size() - 1).getStopIndex()) {
            return CursorTokenInfo.noFindCursorToken();
        }

        // 二分查找的左右边界初始化。
        int left = 0;
        int right = tokenStream.size() - 1;

        // 用于存储上下文信息的对象,包括前置 Token 和连续 "."。
        PreviousTokens previousTokens = new PreviousTokens();

        // 使用二分查找定位光标所在的 Token。
        while (left <= right) {
            int mid = (left + right) >>> 1; // 等价于 (left + right) / 2,但防止溢出。
            Token token = tokenStream.get(mid);

            // 判断光标是否在当前 Token 范围内。
            if (isCursorWithinToken(currentPosition, token)) {
                // 根据光标在 Token 的位置(起始、中间、结束)确定光标相对位置。
                CursorTokenInfo.TokenPosition position = determinePosition(currentPosition, token);

                // 如果当前 Token 是符号且不是 ".",为其创建一个空格 Token 替代。
//                if (isSymbol(token)) {
//                    token = createSpaceToken(token);
//                }

                // 处理上下文信息,仅在当前 Token 是 "." 时收集连续 "."。
                if (mid > 0 && StringUtils.equals(".", token.getText())) {
                    // 设置前置 Token。
                    Token previousToken = tokenStream.get(mid - 1);
                    previousTokens.setPreviousToken(previousToken);

                    // 收集前置连续 "." 的 Token 列表。
                    List<Token> tokenList = new ArrayList<>();
                    for (int backwardIndex = mid - 1; backwardIndex >= 0; backwardIndex--) {
                        Token backwardToken = tokenStream.get(backwardIndex);
                        if (".".equals(backwardToken.getText())) {
                            tokenList.add(backwardToken);
                        } else {
                            break;
                        }
                    }
                    previousTokens.setPreviousTokens(tokenList);
                }

                // 返回包含上下文信息的结果。
                return new CursorTokenInfo(previousTokens, token, position);
            }

            // 根据光标位置调整二分查找范围。
            if (currentPosition < token.getStartIndex()) {
                right = mid - 1; // 光标在当前 Token 左侧。
            } else {
                left = mid + 1; // 光标在当前 Token 右侧。
            }
        }

        // 如果未找到匹配的令牌,返回默认结果。
        return CursorTokenInfo.noFindCursorToken();
    }

    /**
     * 判断光标是否位于 Token 的范围内。
     *
     * @param currentPosition 光标位置。
     * @param token           当前令牌。
     * @return 如果光标在令牌范围内(包括起始和结束位置),返回 true;否则返回 false。
     */
    private static boolean isCursorWithinToken(int currentPosition, Token token) {
        return currentPosition >= token.getStartIndex() && currentPosition <= token.getStopIndex();
    }

    /**
     * 根据光标位置相对于 Token 的范围,确定光标的相对位置。
     *
     * @param currentPosition 光标位置。
     * @param token           当前令牌。
     * @return 光标在令牌中的相对位置,可能是 START、MIDDLE 或 END。
     */
    private static CursorTokenInfo.TokenPosition determinePosition(int currentPosition, Token token) {
        if (currentPosition == token.getStartIndex()) {
            return CursorTokenInfo.TokenPosition.START;
        } else if (currentPosition == token.getStopIndex()) {
            return CursorTokenInfo.TokenPosition.END;
        } else {
            return CursorTokenInfo.TokenPosition.MIDDLE;
        }
    }

    /**
     * 判断一个 Token 是否是符号(非字母数字且不是 ".")。
     *
     * @param token 当前令牌。
     * @return 如果 Token 是符号且不是 ".",返回 true;否则返回 false。
     */
    private static boolean isSymbol(Token token) {
        String text = token.getText();
        return text != null && !text.isEmpty() && !text.equals(".") && !Character.isLetterOrDigit(text.charAt(0));
    }

    /**
     * 创建一个空格 Token,用于替代符号 Token。
     *
     * @param originalToken 原始符号 Token。
     * @return 表示空格的 Token,其位置与原始 Token 起始位置相同。
     */
    private static Token createSpaceToken(Token originalToken) {
        // 使用原始 Token 的输入源和字符流构造一个新的 Token。
        Pair<TokenSource, CharStream> sourcePair = new Pair<>(originalToken.getTokenSource(), originalToken.getInputStream());
        CommonToken spaceToken = new CommonToken(sourcePair, originalToken.getType(), originalToken.getChannel(),
                originalToken.getStartIndex(), originalToken.getStartIndex());
        spaceToken.setText(" "); // 设置 Token 的文本内容为单个空格。
        return spaceToken;
    }
}
/**
 * 用于记录光标当前位置相关的 Token 信息。
 * <p>
 * 该类提供光标当前 Token 的详细上下文信息,包括:
 * 1. 当前 Token 的索引和位置(开始、中间、结束)。
 * 2. 与当前 Token 相关的上下文 Token 信息(如 PreviousTokens)。
 * </p>
 * <p>
 * 字段说明:
 * <ul>
 *     <li><b>previousToken</b> - 当前光标位置之前的上下文 Token 信息。</li>
 *     <li><b>currentToken</b> - 当前光标位置的 Token,可用于快速判断一些场景,例如:
 *     <ul>
 *         <li>是否处于注释中。</li>
 *         <li>是否处于字符串中。</li>
 *         <li>是否处于特殊数字串中(如 b'42141')。</li>
 *     </ul>
 *     </li>
 *     <li><b>tokenIndex</b> - 当前 Token 的索引,通常为 CompletionCord 必需的参数。</li>
 *     <li><b>position</b> - 光标在当前 Token 中的位置,可为 START、MIDDLE 或 END。</li>
 * </ul>
 * <p>
 * 特殊方法:
 * <ul>
 *     <li><b>noFindCursorToken()</b> - 用于生成默认的 CursorTokenInfo,当未找到匹配的 Token 时返回。</li>
 * </ul>
 */
@Data
public class CursorTokenInfo {

    private PreviousTokens previousToken;

    /**
     * 当前光标所在位置的 Token。
     * 便于判断是否处于注释、字符串或特殊数字串中。
     */
    private Token currentToken;

    /**
     * 当前 Token 的索引,通常为 CompletionCord 必需的参数。
     */
    private int tokenIndex;

    /**
     * 光标在当前 Token 中的位置。
     * 可以为以下值之一:
     * <ul>
     *     <li>START - 位于 Token 的开头。</li>
     *     <li>MIDDLE - 位于 Token 的中间。</li>
     *     <li>END - 位于 Token 的结尾。</li>
     * </ul>
     */
    private TokenPosition position;

    public static CursorTokenInfo noFindCursorToken() {
        CursorTokenInfo cursorTokenInfo = new CursorTokenInfo();
        cursorTokenInfo.currentToken = null;
        cursorTokenInfo.setTokenIndex(0);
        cursorTokenInfo.setPosition(TokenPosition.START);
        return cursorTokenInfo;
    }

    public boolean found() {
        return currentToken != null;
    }

    public CursorTokenInfo() {
    }

    public CursorTokenInfo(PreviousTokens previousToken, Token currentToken, TokenPosition position) {
        this.previousToken = previousToken;
        this.currentToken = currentToken;
        this.tokenIndex = currentToken.getTokenIndex();
        this.position = position;
    }

    /**
     * 光标在当前 Token 中的位置。
     */
    public enum TokenPosition {
        START, MIDDLE, END
    }
}
/**
 * 用于记录与当前 Token 相关的上下文 Token 信息。
 * <p>
 * 功能说明:
 * 如果当前 Token 是一个 ".",则:
 * 1. `previousToken` 记录当前 "." 前的一个 Token;
 * 2. `previousTokens` 按顺序记录从当前 "." 开始向前所有连续的 Token,直到遇到非 "." 为止。
 * </p>
 *
 * 示例:
 * <pre>
 * SQL: select * from test.public.
 * 当前 Token: "."
 * 解析结果:
 * - previousToken: "public"
 * - previousTokens: ["test", "."]
 * </pre>
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PreviousTokens {
    private Token previousToken;
    private List<Token> previousTokens;
}


    private boolean isTipPosition(CursorTokenInfo cursorTokenInfo) {
        CursorTokenInfo.TokenPosition position = cursorTokenInfo.getPosition();
        if (position.equals(CursorTokenInfo.TokenPosition.START)) {
            return isTipStart(cursorTokenInfo);
        } else if (position.equals(CursorTokenInfo.TokenPosition.MIDDLE)) {
            return isTipMiddle(cursorTokenInfo);
        } else if (position.equals(CursorTokenInfo.TokenPosition.END)) {
            return isTipEnd(cursorTokenInfo);
        }
        return true;
    }
   @Override
    protected boolean isTipEnd(CursorTokenInfo cursorTokenInfo) {
        //  判断是不是处于单行注释,多行注释,以及字符串内
        Token currentToken = cursorTokenInfo.getCurrentToken();
        int tokenType = currentToken.getType();
        //  这个是判断是不是在注释里面的
        if (COMMENT_TOKENS.contains(tokenType)) {
            return false;
        }

        return !NUMBER_TOKENS.contains(tokenType);

    }

    @Override
    protected boolean isTipMiddle(CursorTokenInfo cursorTokenInfo) {
        //  判断是不是处于单行注释,多行注释,以及字符串内
        Token currentToken = cursorTokenInfo.getCurrentToken();
        int tokenType = currentToken.getType();
        //  这个是判断是不是在注释里面的
        if (COMMENT_TOKENS.contains(tokenType)) {
            return false;
        }
        // 这个是判断是不是处于字符串规则
        if (STRING_TOKENS.contains(tokenType)) {
            return false;
        }
        return !NUMBER_TOKENS.contains(tokenType);
    }

    @Override
    protected boolean isTipStart(CursorTokenInfo cursorTokenInfo) {
        //  判断是不是处于单行注释,多行注释,以及字符串内
        Token currentToken = cursorTokenInfo.getCurrentToken();
        int tokenType = currentToken.getType();
        //
   /*  if (IGNORE_TOKENS.contains(tokenType)) {
            return false;
        }*/
        //  这个是判断是不是在注释里面的
        return !COMMENT_TOKENS.contains(tokenType);
    }

@openai0229
Copy link
Author

其实就是简单判断是不是那些定义的token而已,之后我这里,发现当前token如果是一个"."的话,我就会去往前遍历一下,收集一下对应的database以及scheme信息(当前这个取决于数据库特性),但是这个办法没法解决提示字段的情况

@openai0229
Copy link
Author

image 今天刚加的bug,哈哈哈

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants