如何自定义JLine的 Less内置分页提示符?

后端开发 2026-07-07

是否有方法自定义JLine的 Less内置分页器输出的分页提示符?

查看JLine的 Less的源代码时,我没有发现对 -P选项的支持(这正是经典的 less 命令所支持的)。

我的目标是在默认分页提示符中为用户在查看文本时提供一些提示。例如,像这样的内容:'q' to Quit, <Space> for more, 'h' for Help

目前,当从 InputStream 读取时,JLine的 Less只是为分页提示符打印一个 :,而不熟悉分页器的用户,尤其是 less,可能不知道该怎么做。

我正在使用JLine 3.30。无法升级到4.x,因为我需要支持Java 8运行时,但我查看了4.x发行版中的Less源码,仍找不到自定义分页提示符的选项。文档注释非常少。

解决方案

你是对的——JLine的 Less不支持GNU less的 -P(自定义提示符)选项。提示符在display() 方法中是硬编码的:当没有活动消息(搜索结果、错误、(END) 等)时,它只是渲染成:。

相关代码在 Less.java

} else if (message != null) {
    msg.style(AttributedStyle.INVERSE);
    msg.append(message);
    msg.style(AttributedStyle.INVERSE.inverseOff());
} else if (displayPattern != null) {
    msg.append("&");
} else {
    msg.append(":");   // ← this is the default prompt
}

变通方法:在同一包中对Less进行子类化

display() 方法是包私有的,但消息字段是受保护的。你可以在 org.jline.builtins 包内从 Less 创建子类(在你自己的源码树中——Java允许在类路径上拆分包),并覆盖display() 以注入一个自定义的默认提示符:

package org.jline.builtins;

import java.io.IOException;
import java.nio.file.Path;

import org.jline.utils.ConfigurationPath;

/**
 * A Less pager with a customizable default prompt.
 */
public class CustomLess extends Less {

    private final String defaultPrompt;

    public CustomLess(Terminal terminal, Path currentDir, Options opts,
                      ConfigurationPath configPath, String defaultPrompt) {
        super(terminal, currentDir, opts, configPath);
        this.defaultPrompt = defaultPrompt;
    }

    @Override
    synchronized boolean display(boolean oneScreen) throws IOException {
        if (message == null && buffer.length() == 0 && displayPattern == null) {
            message = defaultPrompt;
        }
        return super.display(oneScreen);
    }

    @Override
    synchronized boolean display(boolean oneScreen, Integer curPos) throws IOException {
        if (message == null && buffer.length() == 0 && displayPattern == null) {
            message = defaultPrompt;
        }
        return super.display(oneScreen, curPos);
    }
}

使用它

不要调用 Commands.less(...),直接创建并运行你的 CustomLess。设置很简单——下面是对 Commands.less() 在内部所做的修改(改编自此):

import org.jline.builtins.*;
import org.jline.builtins.Source.StdInSource;
import org.jline.builtins.Source.URLSource;
import org.jline.terminal.Terminal;
import org.jline.utils.ConfigurationPath;

import java.io.InputStream;
import java.nio.file.Path;
import java.util.*;

public void showInPager(Terminal terminal, InputStream content, 
                        Path currentDir, ConfigurationPath configPath) throws Exception {
    Options opt = Options.compile(Less.usage()).parse(new Object[0]);
    CustomLess less = new CustomLess(
        terminal, currentDir, opt, configPath,
        "'q' to Quit, <Space> for more, 'h' for Help"
    );
    List<Source> sources = new ArrayList<>();
    sources.add(new StdInSource(content));
    less.run(sources);
}

自定义的提示符将以反显显示(与JLine对 (END) 和文件信息消息使用的样式相同),这使其作为状态栏更突出。

工作原理

Less.run() 的主事件循环在每次按键前都会重置 message = null,然后调用 display(false) 来重绘界面。我们的覆盖会拦截每次 display() 调用:如果没有其他消息处于活动状态(搜索结果、错误、(END) 等),它会把消息设为自定义提示符。当设置了真实的消息时——例如“未找到模式”或“(END)”——覆盖保持原样,因此保留了普通Less的行为。

关于拆分包的说明

这种方法会把你的类放在 org.jline.builtins,这与JLine自身的类处于同一个包中。在类路径上这通常没问题(这是JLine 3.x的常见设置)。如果你将来迁移到Java模块路径(JPMS),拆分包将不被允许,你需要另一种实现方式。

已提交的功能请求

我已在jline/jline3#2033提交了一个请求,为Less添加一个合适的defaultPrompt API。拟议的变更很小——一个公开的setter,以及在display() 中的一行修改——并会让你在不需要上述拆分包的花招的情况下编写 less.defaultPrompt("'q' to Quit, <Space> for more, 'h' for Help")。如果这对你有用,请给该问题点个 👍,以帮助它获得优先处理。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章