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

news/2024/7/4 8:30:46

效果
在这里插入图片描述

主要类(继承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

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


http://www.niftyadmin.cn/n/658014.html

相关文章

美国男子麦当劳快餐吃出死老鼠 索赔170万美元

【来源&#xff1a;中国新闻网】 【作者&#xff1a;钟岩】 美国得克萨斯州一名男子26日向联邦地方法院起诉麦当劳&#xff0c;其原因是他与家人在麦当劳的外带快餐里发现一只死老鼠。   美国达拉斯早晨新闻报道说&#xff0c;这位名为托德-哈利的男子向麦当劳索赔170万美元…

background-size css background-images

在设计网页时&#xff0c;经常会用到背景图片来达到视觉效果。 一般情况下用repeat的方式是最适全不过了&#xff0c;不过有时间是采用整图来充当背景&#xff0c;那么这个时候就会有多种可能性的存在了。 整图来做背景一般是采用no-repeat来实现的&#xff0c;但是屏幕大小是比…

WPF下递归生成树形数据绑定到TreeView上(转)

WPF下递归生成树形数据绑定到TreeView上 最终效果图&#xff1a;&#xff08;用于学习类的效果 图片丑了点&#xff0c;看官莫怪&#xff09; 新建窗体 然后在前端适当位置插入如下代码&#xff1a; <TreeView x:Name"departmentTree" Height"500" Wid…

使用JTextArea的游戏测试

效果 核心类(主要是使用char[][]存储显示信息,StringBuilder用于拼接信息在JTextArea上显示) public class MyTextPanel extends JTextArea implements MyListener,Runnable, Input {//宽 88 高 29private StringBuilder builder;private char[][] map;private int updateT…

Jersey 2.x 服务器端应用支持的容器

基于 JAX-RS Servlet-based 部署的一部分标准&#xff0c;能运行在任何支持 Servlet 2.5 和更高标准的的容器上。Jersey 提供支持程序化部署在下面的容器中&#xff1a;Grizzly 2 &#xff08;HTTP 和 Servlet&#xff09;&#xff0c; JDK Http server&#xff0c;Simple Http…

一个用xml存储日志信息的程序

本程序中利用asp代码生成的日志信息存储到xml文件中&#xff0c;然后由xslt格式化输出 该程序共分三个部分:1. 生成日志的asp程序 代码见以前的一篇文章&#xff1a; http://blog.csdn.net/precipitant/archive/2005/04/28/366634.aspx 2. 由 上面的 程序 生成的日志文…

properties与yaml互转工具类

没事写的&#xff0c;实际更加推荐使用网上一些更加完善的在线工具 效果 正向转换 原内容 转换结果 反向 原内容 结果 主要类 public class p2y {private static final String REVERSE "-r";private static final Pattern PATTERN Pattern.compile("(.)\…

IDEA default keymap整理

main menu ctrlalts 打开settings ctrlaltshifts 打开project Structure alt1 打开项目目录 alt7 打开Structure目录 altf12 代开teminal git ctrlaltz revert ctrlk commit project ctrlshiftk push edit ctrly删除行 ctrldelete删除到单词末尾 ctrlbackspace删除到单词首 ctr…