Skip to content

Conversation

@czpilar
Copy link
Contributor

@czpilar czpilar commented Dec 27, 2025

Improved #1246

…olving key=value pairs in the command line.

Improved spring-projects#1246

Signed-off-by: czpilar <david@czpilar.net>
@czpilar
Copy link
Contributor Author

czpilar commented Dec 27, 2025

I’ve just realized that after fixing the completion issue #1246, I completely missed the case where key=value is already present on the command line.

So if you already have shell:>hello --first=Mary on the line and press Tab, you will see the completion proposals for the --first and -f options again.

Consider the following example:

package org.springframework.shell.samples.helloworld.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.core.command.CommandOption;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.shell.core.command.annotation.Option;
import org.springframework.shell.core.command.completion.CompletionProposal;
import org.springframework.shell.core.command.completion.CompletionProvider;

import java.util.Collections;
import java.util.stream.Stream;

@SpringBootApplication
public class SpringShellApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringShellApplication.class, args);
    }

    @Command(name = "hello", completionProvider = "helloNameCompletionProvider")
    public void sayHello(@Option(longName = "first", shortName = 'f') String first,
                         @Option(longName = "last", shortName = 'l') String last) {
        System.out.println("Hello " + first + " " + last + "!");
    }

    @Bean
    public CompletionProvider helloNameCompletionProvider() {
        return completionContext -> {
            CommandOption option = completionContext.getCommandOption();
            if (option == null) {
                return Collections.emptyList();
            }

            Stream<String> options = Stream.empty();

            if ("first".equals(option.longName())) {
                options = Stream.of("Peter", "Paul", "Mary");
            }
            else if ("last".equals(option.longName())) {
                options = Stream.of("Chan", "Noris");
            }

            return options.map(CompletionProposal::new).toList();
        };
    }

}

If you also want completion for the key=value pair, such as shell:>hello --first=, and then press Tab, it can be provided by the helloNameCompletionProvider as well.

Consider the following example:

package org.springframework.shell.samples.helloworld.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.core.command.CommandOption;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.shell.core.command.annotation.Option;
import org.springframework.shell.core.command.completion.CompletionProposal;
import org.springframework.shell.core.command.completion.CompletionProvider;

import java.util.Collections;
import java.util.stream.Stream;

@SpringBootApplication
public class SpringShellApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringShellApplication.class, args);
	}

	@Command(name = "hello", completionProvider = "helloNameCompletionProvider")
	public void sayHello(@Option(longName = "first", shortName = 'f') String first,
			@Option(longName = "last", shortName = 'l') String last) {
		System.out.println("Hello " + first + " " + last + "!");
	}

	@Bean
	public CompletionProvider helloNameCompletionProvider() {
		return completionContext -> {
			CommandOption option = completionContext.getCommandOption();
			if (option == null) {
				return Collections.emptyList();
			}

			String word = completionContext.getWords().get(completionContext.getWords().size() - 1);
			if (word.contains("=")) {
				word = word.substring(0, word.indexOf('='));
			}
			String prefix = word.isEmpty() ? word : word + "=";

			Stream<String> options = Stream.empty();

			if ("first".equals(option.longName())) {
				options = Stream.of("Peter", "Paul", "Mary");
			}
			else if ("last".equals(option.longName())) {
				options = Stream.of("Chan", "Noris");
			}

			return options.map(str -> prefix + str).map(CompletionProposal::new).toList();
		};
	}

}

@fmbenhassine Also, it’s great that you improved the default command parser in #1248 - it now works very well with completion.

@czpilar czpilar force-pushed the 1246-completion-for-particular-command-2 branch from 3cf436a to e1964aa Compare December 27, 2025 09:00
@czpilar
Copy link
Contributor Author

czpilar commented Dec 27, 2025

Added tests for Command Completer.

Signed-off-by: czpilar <david@czpilar.net>
@czpilar czpilar force-pushed the 1246-completion-for-particular-command-2 branch from e1964aa to af0e0ff Compare December 27, 2025 09:05
@czpilar
Copy link
Contributor Author

czpilar commented Dec 27, 2025

Added more tests.

Signed-off-by: czpilar <david@czpilar.net>
@czpilar czpilar force-pushed the 1246-completion-for-particular-command-2 branch from 36eb7c7 to 2a158b3 Compare December 27, 2025 09:32
Signed-off-by: czpilar <david@czpilar.net>
@czpilar
Copy link
Contributor Author

czpilar commented Dec 27, 2025

Added one more test for command with subcommand

Signed-off-by: czpilar <david@czpilar.net>
@czpilar czpilar force-pushed the 1246-completion-for-particular-command-2 branch from c1b7a74 to ab14f94 Compare December 27, 2025 11:37
@czpilar
Copy link
Contributor Author

czpilar commented Dec 27, 2025

Added one more tests for completion with two options where one is subset of other

@fmbenhassine
Copy link
Contributor

That's great! I am glad things are getting improved continuously.

Thank you very much for your PR 👍

@fmbenhassine
Copy link
Contributor

If you also want completion for the key=value pair, such as shell:>hello --first=, and then press Tab, it can be provided by the helloNameCompletionProvider as well.

Thank you for sharing this example! Very useful for those who want to provide completion values with the --key=value notation. I am thinking about #285 here as well.

Rebased, squashed and merged as 8960cf4. Thank you for your contribution 🙏

@czpilar czpilar deleted the 1246-completion-for-particular-command-2 branch December 28, 2025 14:51
@czpilar
Copy link
Contributor Author

czpilar commented Dec 28, 2025

Thank you for sharing this example! Very useful for those who want to provide completion values with the --key=value notation. I am thinking about #285 here as well.

@fmbenhassine I can take a look at this. You can assign #285 to me... I will look at this later today or tomorrow.

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

Successfully merging this pull request may close these issues.

2 participants