Skip to content

Commit 32f5b5f

Browse files
authored
Merge pull request #4 from rage/dev
Merge dev into main - adds completion script functionalities
2 parents 04db521 + b4e35fe commit 32f5b5f

6 files changed

Lines changed: 203 additions & 75 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ authors = ["HoolaBoola <jaime.heikkiladias@helsinki.fi>",
88
edition = "2018"
99
description = "Command line interface for TMC, written in Rust."
1010
license = "Apache-2.0"
11+
build = "build.rs"
1112

1213
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1314

@@ -35,3 +36,6 @@ crossterm = "0.19"
3536
tui = { version = "0.14", default-features = false, features = ['crossterm'] }
3637
termcolor = "1.1"
3738
terminal_size = "0.1.16"
39+
40+
[build-dependencies]
41+
clap = "2.3.3"

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ To use the software from any directory, you can add it to your environmental var
7676

7777
echo "alias tmc='<directory>/<executable name>'" >> "$HOME/.bashrc"
7878

79+
#### For macOS
80+
81+
The instructions for Linux **should** work on macOS as well. This has not been tested yet, though, so your mileage may vary.
82+
7983
#### For Windows:
8084

8185
After downloading the executable you can start using it from the command line by navigating to the directory it resides at.
@@ -84,6 +88,12 @@ To be able to use it from any directory, you can add it to your environmental va
8488

8589
set PATH=%PATH%;<directory>
8690

91+
#### Tab-completion from commandline
92+
93+
You can generate shell completion scripts by running `tmc generate-completions --[bash/zsh/powershell] > /path/to/your/completions/directory/filename`. For `bash`, `filename` should be `tmc.bash`. For `zsh`, `_tmc`. This has not yet been tested for Powershell, but the script *should* work if placed in the appropriate directory.
94+
95+
Make sure to have the appropriate software configuration for completions to work. For Zsh and Powershell, completions should be supported by default, but for Bash you may need to install `bash-completion` or similar packages and follow their documentation.
96+
8797
## Commands
8898

8999
tmc [FLAGS] [SUBCOMMAND]

build.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use clap::Shell;
2+
3+
include!("src/cli.rs");
4+
5+
fn main() {
6+
let outdir = match std::env::var_os("OUT_DIR") {
7+
None => {
8+
println!("Hellooo");
9+
return;
10+
}
11+
Some(outdir) => outdir,
12+
};
13+
14+
println!("{:?}", outdir);
15+
16+
let mut app = build_cli();
17+
app.gen_completions("tmc", Shell::Bash, &outdir);
18+
app.gen_completions("tmc", Shell::PowerShell, &outdir);
19+
app.gen_completions("tmc", Shell::Zsh, &outdir);
20+
}

scripts/install.sh

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
#!/bin/bash
2+
23
set -euo pipefail
34

45
echo "~ Installing TMC-CLI ~"
56
echo "(If your shell is not bash, you may have to do the installation manually.)"
67
echo ""
78

8-
if (( $# < 2 )); then
9-
echo "You need to give architecture (x86_64/i686) and OS (mac, linux) as arguments."
10-
exit 1
11-
fi
9+
os="$(uname -s)"
10+
platform="$(uname -m)"
1211

13-
# Get platform-string from first argument, OS from the second
14-
platform=$1
15-
os=$2
12+
if (( $# = 2 )); then
13+
# Get platform-string from first argument, OS from the second
14+
platform=$1
15+
os=$2
16+
fi
1617

1718
echo "Fetching latest version URL from https://download.mooc.fi"
1819
if ! PAGE=$(curl -s https://download.mooc.fi); then
@@ -24,7 +25,7 @@ fi
2425
PAGE=$(echo $PAGE | sed -r 's:</Contents><Contents>:</Contents> <Contents>:g')
2526

2627
fileprefx=""
27-
if [[ "$os" == "mac" ]]; then
28+
if [[ "$os" == "Darwin" ]]; then
2829
fileprefx="tmc-cli-rust-${platform}-apple-darwin-v"
2930
else
3031
fileprefx="tmc-cli-rust-${platform}-unknown-linux-gnu-v"
@@ -94,19 +95,69 @@ elif [ "$SHELLNAME" = "csh" ] || [ "$SHELLNAME" = "tcsh" ]; then
9495
echo "Please add manually variables from $PROFILEFILE to your .cshrc using csh syntax."
9596
else
9697
PROFILEFILE=$HOME/.shrc
97-
echo "Defaulting to .shrc for environment variables, if this is incorrect, please copy these manually to correct file."
98+
echo "Defaulting to .shrc for environment variables. If this is incorrect, please copy these manually to correct file."
9899
fi
99-
# Removes old aliases
100+
# Removes old aliases and such
100101
sed -i '/alias tmc=/d' "$PROFILEFILE"
102+
sed -i "/export TMC_LANGS_CONFIG_DIR=/d" "$PROFILEFILE"
101103

102104
echo $PROFILEFILE
103105

106+
COMPLETIONS_PATH=$HOME/.local/share/tmc-autocomplete
107+
108+
109+
CMD=$PWD/$filename
110+
104111
# Saves new alias to .bashrc
105-
echo "alias tmc='$PWD/$filename'" >> "$PROFILEFILE"
112+
echo "alias tmc='$CMD'" >> "$PROFILEFILE"
106113
echo "export TMC_LANGS_CONFIG_DIR='$HOME/tmc-config'" >> "$PROFILEFILE"
107114

108115
echo ""
109116

117+
118+
119+
#
120+
#
121+
# Auto-complete scripts
122+
#
123+
#
124+
if [ "$SHELLNAME" = "bash" ]; then
125+
126+
127+
echo "Generating auto-complete scripts to $COMPLETIONS_PATH"
128+
echo ""
129+
echo ""
130+
131+
# removing possibly existing sourcing
132+
sed -i '/source/!b;/tmc-autocomplete/d' "$PROFILEFILE"
133+
134+
135+
# creating the completions directory, if it doesn't exist
136+
eval "mkdir -p $COMPLETIONS_PATH"
137+
138+
# calling the generate-completions subcommand to generate the completion script
139+
eval "$CMD generate-completions --bash > $COMPLETIONS_PATH/tmc.bash"
140+
141+
# adding the line to .bashrc so that bash knows where to look for
142+
echo "source $COMPLETIONS_PATH/tmc.bash" >> "$PROFILEFILE"
143+
144+
elif [ "$SHELLNAME" = "zsh" ]; then
145+
echo "Generating auto-complete scripts to $COMPLETIONS_PATH"
146+
echo ""
147+
echo ""
148+
149+
# removing possibly existing definitions
150+
sed -i "/compdef _tmc/d" "$PROFILEFILE"
151+
sed -i '/fpath/!b;/tmc-autocomplete/d' "$PROFILEFILE"
152+
153+
eval "mkdir -p $COMPLETIONS_PATH"
154+
eval "$CMD generate-completions --bash > $COMPLETIONS_PATH/_tmc"
155+
156+
echo "fpath=($COMPLETIONS_PATH/_tmc " '$fpath)' >> "$PROFILEFILE"
157+
158+
echo "compdef _tmc tmc" >> "$PROFILEFILE"
159+
fi
160+
110161
echo "Installation complete. Please restart the terminal."
111162
echo "After opening a new terminal, you can try using TMC-CLI from the command line with:"
112163
echo " 'tmc login'"

src/cli.rs

Lines changed: 81 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -10,84 +10,84 @@ pub fn build_cli() -> App<'static, 'static> {
1010
.subcommand(SubCommand::with_name("courses").about("List the available courses"))
1111
.subcommand(
1212
SubCommand::with_name("download")
13-
.about("Downloads course exercises")
14-
.arg(
15-
Arg::with_name("course")
16-
.short("c")
17-
.long("course")
18-
.value_name("course name")
19-
.required(false),
13+
.about("Downloads course exercises")
14+
.arg(
15+
Arg::with_name("course")
16+
.short("c")
17+
.long("course")
18+
.value_name("course name")
19+
.required(false),
2020
)
21-
.arg(
22-
Arg::with_name("currentdir")
23-
.short("d")
24-
.long("currentdir")
25-
.required(false),
21+
.arg(
22+
Arg::with_name("currentdir")
23+
.short("d")
24+
.long("currentdir")
25+
.required(false),
2626
),
27-
)
27+
)
2828
.subcommand(
2929
SubCommand::with_name("exercises")
30-
.about("List the exercises for a specific course")
31-
.arg(Arg::with_name("course").value_name("course").required(true)),
32-
)
30+
.about("List the exercises for a specific course")
31+
.arg(Arg::with_name("course").value_name("course").required(true)),
32+
)
3333
.subcommand(
3434
SubCommand::with_name("login")
35-
.about("Login to TMC server")
36-
.arg(
37-
Arg::with_name("non-interactive")
38-
.short("n")
39-
.help("Initiates the non-interactive mode.")
40-
.long("non-interactive"),
35+
.about("Login to TMC server")
36+
.arg(
37+
Arg::with_name("non-interactive")
38+
.short("n")
39+
.help("Initiates the non-interactive mode.")
40+
.long("non-interactive"),
4141
),
42-
)
42+
)
4343
.subcommand(SubCommand::with_name("logout").about("Logout from TMC server"))
4444
.subcommand(
4545
SubCommand::with_name("organization")
46-
.about("Change organization")
47-
.arg(
48-
Arg::with_name("non-interactive")
49-
.short("n")
50-
.help("Initiates the non-interactive mode.")
51-
.long("non-interactive"),
46+
.about("Change organization")
47+
.arg(
48+
Arg::with_name("non-interactive")
49+
.short("n")
50+
.help("Initiates the non-interactive mode.")
51+
.long("non-interactive"),
5252
),
53-
)
53+
)
5454
.subcommand(
5555
SubCommand::with_name("paste")
56-
.about("Submit exercise to TMC pastebin")
57-
.arg(
58-
Arg::with_name("exercise")
59-
.value_name("exercise")
60-
.required(false),
56+
.about("Submit exercise to TMC pastebin")
57+
.arg(
58+
Arg::with_name("exercise")
59+
.value_name("exercise")
60+
.required(false),
6161
),
62-
)
62+
)
6363
.subcommand(
6464
SubCommand::with_name("submit")
65-
.about("Submit exercises to TMC server")
66-
.arg(
67-
Arg::with_name("exercise")
68-
.value_name("exercise")
69-
.required(false),
65+
.about("Submit exercises to TMC server")
66+
.arg(
67+
Arg::with_name("exercise")
68+
.value_name("exercise")
69+
.required(false),
7070
),
71-
)
71+
)
7272
.subcommand(
7373
SubCommand::with_name("test")
74-
.about("Run local exercise tests")
75-
.arg(
76-
Arg::with_name("exercise")
77-
.value_name("exercise")
78-
.required(false),
74+
.about("Run local exercise tests")
75+
.arg(
76+
Arg::with_name("exercise")
77+
.value_name("exercise")
78+
.required(false),
7979
),
80-
)
80+
)
8181
.subcommand(
8282
SubCommand::with_name("fetchupdate")
83-
.setting(AppSettings::Hidden)
84-
.about("Finishes the autoupdater. Administator rights needed."),
85-
)
83+
.setting(AppSettings::Hidden)
84+
.about("Finishes the autoupdater. Administator rights needed."),
85+
)
8686
.subcommand(
8787
SubCommand::with_name("cleartemp")
88-
.setting(AppSettings::Hidden)
89-
.about("Removes tempfiles. Administator rights needed."),
90-
)
88+
.setting(AppSettings::Hidden)
89+
.about("Removes tempfiles. Administator rights needed."),
90+
)
9191
.subcommand(
9292
SubCommand::with_name("elevateddownload")
9393
.setting(AppSettings::Hidden)
@@ -111,13 +111,33 @@ pub fn build_cli() -> App<'static, 'static> {
111111
.subcommand(SubCommand::with_name("update").about("Update exercises"))
112112
.arg(
113113
Arg::with_name("no-update")
114-
.short("d")
115-
.long("no-update")
116-
.help("Disable auto update temporarily"),
117-
)
114+
.short("d")
115+
.long("no-update")
116+
.help("Disable auto update temporarily"),
117+
)
118118
.arg(
119119
Arg::with_name("testmode")
120-
.long("testmode")
121-
.help("Only for internal testing, disables server connection"),
122-
)
120+
.long("testmode")
121+
.help("Only for internal testing, disables server connection"),
122+
)
123+
.subcommand(SubCommand::with_name("generate-completions")
124+
.usage("tmc generate_completions --[your shell] > /path/to/your/completions/folder")
125+
.about("Generate completion scripts for command line usage.")
126+
.setting(AppSettings::DisableVersion)
127+
.setting(AppSettings::Hidden)
128+
.setting(AppSettings::DeriveDisplayOrder)
129+
.arg(
130+
Arg::with_name("bash")
131+
.short("b")
132+
.long("bash"))
133+
.arg(
134+
Arg::with_name("zsh")
135+
.short("z")
136+
.long("zsh")
137+
)
138+
.arg(
139+
Arg::with_name("powershell")
140+
.short("p")
141+
.long("powershell"))
142+
)
123143
}

src/main.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
use clap::{ArgMatches, Shell};
12
use termcolor::{BufferWriter, ColorChoice};
23

3-
use std::io::{stdin, stdout};
4+
use std::{
5+
io,
6+
io::{stdin, stdout},
7+
};
48

59
pub mod io_module;
610
use io_module::IoProduction;
@@ -11,8 +15,12 @@ pub mod progress_reporting;
1115
mod updater;
1216

1317
fn main() {
14-
let matches = cli::build_cli().get_matches();
15-
18+
let cli = cli::build_cli();
19+
let matches = cli.get_matches();
20+
if matches.is_present("generate-completions") {
21+
generate_completions(&matches);
22+
return;
23+
}
1624
let mut stdout = stdout();
1725
let mut stdin = stdin();
1826

@@ -38,3 +46,18 @@ fn main() {
3846
}
3947
commands::handle(&matches, &mut io);
4048
}
49+
50+
fn generate_completions(matches: &ArgMatches) {
51+
let matches = matches.subcommand_matches("generate-completions").unwrap();
52+
let shell = if matches.is_present("bash") {
53+
Shell::Bash
54+
} else if matches.is_present("zsh") {
55+
Shell::Zsh
56+
} else if matches.is_present("powershell") {
57+
Shell::PowerShell
58+
} else {
59+
return;
60+
};
61+
62+
cli::build_cli().gen_completions_to("tmc", shell, &mut io::stdout());
63+
}

0 commit comments

Comments
 (0)