
org.aesh.readline.completion.CompletionHandler Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.aesh.readline.completion;
import org.aesh.readline.Buffer;
import org.aesh.readline.InputProcessor;
import org.aesh.readline.action.mappings.ActionMapper;
import org.aesh.readline.terminal.formatting.TerminalString;
import org.aesh.utils.Config;
import org.aesh.readline.util.Parser;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
/**
* @author Ståle W. Pedersen
*/
public abstract class CompletionHandler {
private CompletionStatus status = CompletionStatus.COMPLETE;
private int displayCompletionSize = 100;
private final List completionList;
private Function aliasHandler;
public CompletionHandler() {
completionList = new ArrayList<>();
}
public void addCompletion(Completion completion) {
completionList.add(completion);
}
public void removeCompletion(Completion completion) {
completionList.remove(completion);
}
public void clear() {
completionList.clear();
}
public CompletionStatus completionStatus() {
return status;
}
public void setCompletionStatus(CompletionStatus status) {
this.status = status;
}
public void setAskCompletionSize(int size) {
displayCompletionSize = size;
}
public int getAskCompletionSize() {
return displayCompletionSize;
}
public void setAliasHandler(Function aliasHandler) {
this.aliasHandler = aliasHandler;
}
public void addCompletions(List completions) {
if(completions != null && completions.size() > 0)
this.completionList.addAll(completions);
}
public abstract C createCompleteOperation(String buffer, int cursor);
/**
* Display possible completions.
* 1. Find all possible completions
* 2. If we find only one, display it.
* 3. If we find more than one, display them,
* but not more than 100 at once
*/
public void complete(InputProcessor inputProcessor) {
if(completionList.size() == 0)
return;
Buffer buffer = inputProcessor.buffer().buffer();
if(completionList.size() < 1)
return;
List possibleCompletions = createCompletionList(buffer);
//LOGGER.info("Found completions: "+possibleCompletions);
if(possibleCompletions.size() == 0) {
//do nothing
}
// only one hit, do a completion
else if(possibleCompletions.size() == 1 &&
possibleCompletions.get(0).getCompletionCandidates().size() == 1) {
//some formatted completions might not be valid and should not be displayed
displayCompletion(
possibleCompletions.get(0).getFormattedCompletionCandidatesTerminalString().get(0),
buffer, inputProcessor,
possibleCompletions.get(0).hasAppendSeparator(),
possibleCompletions.get(0).getSeparator());
}
// more than one hit...
else {
processMultipleCompletions(possibleCompletions, buffer, inputProcessor);
}
}
private List createCompletionList(Buffer buffer) {
List possibleCompletions = new ArrayList<>();
for(int i=0; i < completionList.size(); i++) {
final C co;
if(aliasHandler == null)
co = createCompleteOperation(buffer.asString(), buffer.multiCursor());
else
co = aliasHandler.apply(buffer);
completionList.get(i).complete(co);
if(co.getCompletionCandidates() != null && co.getCompletionCandidates().size() > 0)
possibleCompletions.add(co);
}
return possibleCompletions;
}
private void processMultipleCompletions(List possibleCompletions, Buffer buffer, InputProcessor inputProcessor) {
String startsWith = "";
if(!possibleCompletions.get(0).isIgnoreStartsWith())
startsWith = Parser.findStartsWithOperation(possibleCompletions);
if(startsWith.length() > 0 ) {
if(startsWith.contains(" ") && !possibleCompletions.get(0).doIgnoreNonEscapedSpace())
displayCompletion(new TerminalString(Parser.switchSpacesToEscapedSpacesInWord(startsWith), true),
buffer, inputProcessor,
false, possibleCompletions.get(0).getSeparator());
else
displayCompletion(new TerminalString(startsWith, true), buffer, inputProcessor,
false, possibleCompletions.get(0).getSeparator());
}
// display all
// check size
else {
List completions = new ArrayList<>();
for(int i=0; i < possibleCompletions.size(); i++)
completions.addAll(possibleCompletions.get(i).getCompletionCandidates());
if(completions.size() > 100) {
if(status == CompletionStatus.ASKING_FOR_COMPLETIONS) {
displayCompletions(completions, buffer, inputProcessor);
status = CompletionStatus.COMPLETE;
}
else {
status = CompletionStatus.ASKING_FOR_COMPLETIONS;
inputProcessor.buffer().writeOut(Config.CR);
inputProcessor.buffer().writeOut("Display all " + completions.size() + " possibilities? (y or n)");
}
}
// display all
else {
displayCompletions(completions, buffer, inputProcessor);
}
}
}
/**
* Display the completion string in the terminal.
* If !completion.startsWith(buffer.getLine()) the completion will be added to the line,
* else it will replace whats at the buffer line.
*
* @param completion partial completion
* @param appendSpace if its an actual complete
*/
private void displayCompletion(TerminalString completion, Buffer buffer, InputProcessor inputProcessor,
boolean appendSpace, char separator) {
if(completion.getCharacters().startsWith(buffer.asString())) {
ActionMapper.mapToAction("backward-kill-word").accept(inputProcessor);
inputProcessor.buffer().writeString(completion.toString());
}
else {
inputProcessor.buffer().writeString(completion.toString());
}
if(appendSpace) {
inputProcessor.buffer().writeChar(separator);
}
}
/**
* Display all possible completions
*
* @param completions all completion items
*/
private void displayCompletions(List completions, Buffer buffer,
InputProcessor inputProcessor) {
completions.sort(new CaseInsensitiveComparator());
//if the buffer is longer than one line, we need to move the cursor down the number of lines
//before we continue
if(inputProcessor.buffer().buffer().length() > inputProcessor.buffer().size().getWidth()) {
int numRows = inputProcessor.buffer().buffer().length() / inputProcessor.buffer().size().getWidth();
int cursorRow = inputProcessor.buffer().buffer().cursor() / inputProcessor.buffer().size().getWidth();
for(; cursorRow < numRows; cursorRow++)
inputProcessor.buffer().writeOut(new int[] {27, '[', 'B' });
}
//finally move to a new line
inputProcessor.buffer().writeOut(Config.CR);
//then we print out the completions
inputProcessor.buffer().writeOut(Parser.formatDisplayListTerminalString(completions,
inputProcessor.buffer().size().getHeight(), inputProcessor.buffer().size().getWidth()));
//then on the next line we write the line again
inputProcessor.buffer().drawLineForceDisplay();
}
public enum CompletionStatus {
ASKING_FOR_COMPLETIONS, COMPLETE;
}
private static class CaseInsensitiveComparator implements Comparator {
public int compare(TerminalString s1, TerminalString s2) {
int n1 = s1.getCharacters().length();
int n2 = s2.getCharacters().length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.getCharacters().charAt(i);
char c2 = s2.getCharacters().charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy