用Java实现代码编辑器的下拉补全效果

效果
在这里插入图片描述

主要类(继承JTextArea监听输入,使用JComboBox显示下拉)
可以手动添加候选信息或,使用配置文件,读取配置文件

public class AutoCompleteTextArea extends JTextArea implements AutoCompleteListener {

    private Map<String,TipInfo> map;
    private JComboBox<String> dropDownBox;
    private DefaultComboBoxModel<String> model;
    private int infoWidth;
    private FontMetrics metrics;
    private int fontHeight;
    private int matchLen;

    // 设置提示框宽度
    public void setInfoWidth(int infoWidth) {
        this.infoWidth = infoWidth;
    }

    // 设置字体大小
    @Override
    public void setFont(Font f) {
        super.setFont(f);
        updateFontSize();
    }

    public AutoCompleteTextArea() {
        initData();
        setComponent();
    }

    private void setComponent() {
        add(dropDownBox);
        getDocument().addDocumentListener(this);
        addKeyListener(this);
    }

    private void initData() {
        infoWidth=100;
        map=new HashMap<>();
        model=new DefaultComboBoxModel<>();
        dropDownBox=new JComboBox<>(model);
        updateFontSize();
    }

    private void updateFontSize() {
        metrics = getFontMetrics(getFont());
        fontHeight=metrics.getHeight();
    }

    public void addEntry(String key,TipInfo value){
        map.put(key,value);
    }
    // 直接添加提示信息
    public void addMap(Map<String,TipInfo> map){
        this.map.putAll(map);
    }

    // 加载外部属性文件
    public void loadProperties(Properties properties){
        Set<String> names = properties.stringPropertyNames();
        for (String name:names){
            String property = properties.getProperty(name);
            addEntry(name,getTipInfoByString(property));
        }
    }

    // 添加提示信息
    private TipInfo getTipInfoByString(String property) {
        String[] split = property.split("&");
        if(split.length==1){
            return new TipInfo(split[0]);
        }else {
            return new TipInfo(split[0],Integer.parseInt(split[1]));
        }
    }

    @Override
    public void insertUpdate(DocumentEvent e) {
        if(e.getLength()!=1)return;
        fireTextChange(e.getOffset()+1);
    }

    private void fireTextChange(int pos) {
        String text = getText();
        int start=pos-1;
        while (start>=0&&isChar(text.charAt(start))){
            start--;
        }
        if(start==pos-1)return;//没有进展 不要提示了!!!
        String hotWord = text.substring(start+1,pos);
        model.removeAllElements();
        dropDownBox.setPopupVisible(false);
        //设置符合候选词
        map.keySet().stream().filter(s->s.startsWith(hotWord)).forEach(s->model.addElement(s));
        if(model.getSize()==0)return;
        matchLen=hotWord.length();
        try {
            //显示候选框
            showDropDownBox(text,hotWord.length());
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    // 判断是否为合法字符  减少处理量
    private boolean isChar(char c) {
        return (c>96&&c<123)||(c>64&&c<91);
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
        if(e.getLength()!=1||e.getOffset()==0)return;
        fireTextChange(e.getOffset());
    }

    // 显示下拉框
    private void showDropDownBox(String text,int len) throws BadLocationException {
        int position = getCaretPosition();
        int lineCount = getLineOfOffset(position)+1;//根据当前位置 获取当前所在行
        int offset = getLineStartOffset(lineCount - 1);
        int stringWidth = metrics.stringWidth(text.substring(offset, position - len + 1));
        dropDownBox.setBounds(stringWidth,fontHeight*lineCount,infoWidth,0);
        dropDownBox.setPopupVisible(true);
    }

    // 监听鼠标按键
    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if(keyCode==KeyEvent.VK_SPACE){
            Object selectedItem = dropDownBox.getSelectedItem();
            if(selectedItem!=null){
                int position = getCaretPosition();
                TipInfo tipInfo = map.get(selectedItem.toString());
                //替换范围
                replaceRange(tipInfo.getContent(),position-matchLen,position);
                setCaretPosition(position-matchLen+tipInfo.getPointPos());
                dropDownBox.setSelectedItem(null);
                dropDownBox.setPopupVisible(false);
            }
        }else if(keyCode==KeyEvent.VK_ESCAPE){
            dropDownBox.setSelectedItem(null);
            dropDownBox.setPopupVisible(false);
        }else if(keyCode==KeyEvent.VK_UP||keyCode==KeyEvent.VK_DOWN){
            dropDownBox.dispatchEvent(e);
        }
    }

}

接口监听输入事件

public interface AutoCompleteListener extends DocumentListener, KeyListener {

    @Override
    default void keyTyped(KeyEvent e) {

    }

    @Override
    default void keyReleased(KeyEvent e) {

    }

    @Override
    default void changedUpdate(DocumentEvent e) {

    }

}

配置信息类

public class TipInfo {

    private String content;
    private int pointPos;

    public TipInfo(String content, int pointPos) {
        this.content = content;
        this.pointPos = pointPos;
    }

    public TipInfo(String content) {
        this.content = content;
        pointPos =content.length();
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public int getPointPos() {
        return pointPos;
    }

    public void setPointPos(int pointPos) {
        this.pointPos = pointPos;
    }

}

使用

public class MyIDE extends JFrame implements ActionListener {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(MyIDE::new);
    }

    private JButton create;
    private JTextField name;
    private JTextField args;
    private JButton run;
    private AutoCompleteTextArea textArea;
    private String className;
    private JavaCompiler compiler;
    private static final String BASE_CLASS="public class %s {\n\n\n\n}";

    public MyIDE() throws HeadlessException {
        initData();
        setFrame();
    }

    private void setFrame() {
        setSize(600,400);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        add(textArea);
        JPanel bottom = new JPanel(new FlowLayout());
        bottom.add(new JLabel("类名:"));
        bottom.add(name);
        bottom.add(create);
        bottom.add(new JLabel("参数:"));
        bottom.add(args);
        bottom.add(run);
        add(bottom,BorderLayout.SOUTH);
        setVisible(true);
    }

    private void initData() {
        textArea = new AutoCompleteTextArea();
        setData(textArea);
        create=new JButton("创建");
        create.addActionListener(this);
        name=new JTextField(15);
        args=new JTextField(15);
        run=new JButton("运行");
        run.addActionListener(this);
        compiler = ToolProvider.getSystemJavaCompiler();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource()==create){
            className=name.getText();
            textArea.setText(String.format(BASE_CLASS, className));
        }else if(e.getSource()==run){
            if(className==null)return;
            createFile();
            compiler();

        }
    }

    private void compiler() {
        int res = compiler.run(System.in, System.out, System.err, "src/files/" + className + ".java");
        if(res==0)invoke();
    }

    private void invoke() {
        try {
            Class clazz = new MyClassLoad().findClass(className);
            Method main = clazz.getMethod("main", String[].class);
            String[] array = args.getText().split(",");
            main.invoke(null,new Object[]{array});
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void createFile() {
        try {
            Files.write(Paths.get("src/files",className+".java"),textArea.getText().getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void setData(AutoCompleteTextArea textArea) {
        Properties properties = getProperties();
        textArea.loadProperties(properties);
    }

    private Properties getProperties() {
        Properties properties = new Properties();
        try {
            FileInputStream in = new FileInputStream("src/files/map.properties");
            properties.load(in);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return properties;
    }

    static class MyClassLoad extends ClassLoader{

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] bytes = Files.readAllBytes(Paths.get("src/files", name + ".class"));
                return defineClass(name,bytes,0,bytes.length);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

    }

}

加载的配置文件(使用&分割,后面的数字代表插入内容后光标相对开始的偏移,也可以不指定,光标会处于插入文本的后面)

sout=System.out.println();&19
system=System
string=String
psvm=public static void main(String[] args) {\n\n}&41

最终效果
在这里插入图片描述
还有很多地方需要完善,不过最近比较忙,可能暂时不会再改进

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值