Skip to content

Commit

Permalink
Suggestion providers added
Browse files Browse the repository at this point in the history
  • Loading branch information
mmuzikar committed Feb 15, 2020
1 parent 8ed7bc8 commit eb6e7bb
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 77 deletions.
37 changes: 23 additions & 14 deletions ui/src/components/CompletePrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import { keyDispatcher } from "./TerminalInput";
type Props<T> = {
value: string,
show: boolean,
getData: () => Promise<T[]>,
keys?: {name: string, weight: number}[]
getData: () => Promise<T[]> | undefined,
toggleSending: (arg0:boolean) => void,
fillIn: (arg0:T) => void,
render: (arg0:T) => JSX.Element
};
type State<T> = {
data: T[],
loading: boolean,
currentIndex: number
};

Expand All @@ -24,17 +26,10 @@ export class CompletePrompt<T> extends Component<Props<T>, State<T>> {
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [
{
name: "pattern",
weight: 0.7
}, {
name: "docs",
weight: 0.3
}
]
keys: this.props.keys
}
state = {
loading: false,
data: [],
currentIndex: -1
}
Expand Down Expand Up @@ -73,28 +68,42 @@ export class CompletePrompt<T> extends Component<Props<T>, State<T>> {
}
this.props.toggleSending(outOfRange());
})
this.updateValues();
}

componentDidUpdate(prevProps:Props<T>){
if (this.props.value !== prevProps.value){
this.props.getData().then(val => {
updateValues(){
const val = this.props.getData();
if (val){
this.setState({loading: true});
val.then(val => {
if (val){
const fuse = new Fuse(val, this.fuseOptions);
const results = fuse.search(this.props.value);
this.setState({
data: results as T[],
currentIndex: -1
currentIndex: -1,
loading: false
});
}
})
}
}

componentDidUpdate(prevProps:Props<T>, prevState:State<T>){
if (this.props.value !== prevProps.value){
console.debug("Prop value changed!");
this.updateValues();
}
}

render(){
const offset = (this.state.data.length * 20);
if (!this.props.show){
return <></>;
}
if (this.state.loading){
return <div className="complete-prompt" style={{top: -offset}}>Loading...</div>
}
return <ul className="complete-prompt" style={{top: -offset}}>
{(this.state.data||[]).map((val:T, i) =>
<CompleteEntry<T> setActive={(active) => {
Expand Down
90 changes: 49 additions & 41 deletions ui/src/components/TerminalInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,29 +57,31 @@ export class TerminalInput extends Component<{}, State> {
switch(input.key){
case "Enter":
input.preventDefault();
if (this.state.stepRef !== undefined && this.state.val.length > 0){
if (input.shiftKey){
this.handleSubmit(document.getElementById("terminal-input")!);
//TODO: if user is pressing Enter repeatedly tell them they need to use Shift
} else if (input.shiftKey){
this.handleSubmit(target);
}
// if (this.state.stepRef !== undefined && this.state.val.length > 0){
// this.handleSubmit(document.getElementById("terminal-input")!);
// //TODO: if user is pressing Enter repeatedly tell them they need to use Shift
// } else if (input.shiftKey){
// this.handleSubmit(target);
// }
break;
case "Tab":
input.preventDefault();
if (this.state.stepRef && this.state.stepRef !== undefined && this.getStepRef().args){
let offset = this.state.val.length - this.getStepRef().pattern.length;
//TODO: ugh
let i = 0;
const args = this.getStepRef().args as Argument[];
while(target.selectionStart! < args[i].start!){
i++;
if (i > args.length){
i = 0;
break;
if (this.state.parsedInput.length > 0){
input.preventDefault();
const active = document.activeElement;
if (active && active.classList.contains('arg')){
const id = Number.parseInt(active.getAttribute('tabIndex')!);
const inputs = document.getElementsByClassName('arg');
if (id + 1 >= inputs.length){
(inputs.item(0)! as HTMLElement).focus();
} else {
(inputs.item(id + 1)! as HTMLElement).focus();
}
}
}
break;
break;
case "Escape":
input.preventDefault();
this.setState({
Expand All @@ -88,21 +90,7 @@ export class TerminalInput extends Component<{}, State> {
});
break;
default:
if (this.state.stepRef){
let i = 0;
let accum = 0;
const cursor = target.selectionStart;
for (let val of (this.state.parsedInput as string[])){
accum += val.length;
console.debug(`cursor: ${cursor} accum: ${accum} for val ${val}`);
if (cursor! <= accum){
break;
}
i++;
}
target.selectionStart = cursor;
console.log(`index: ${i} cursor: ${cursor}`);
}

break;
}
keyDispatcher.dispatch(input);
Expand Down Expand Up @@ -143,21 +131,32 @@ export class TerminalInput extends Component<{}, State> {

render(){
let val = this.state.parsedInput.length > 0 && false ? this.state.parsedInput.join("") : this.state.val;
let input = <input autoFocus id="terminal-input" style={{width: "85%"}} value={val} onInput={this.handleChange} onKeyDownCapture={this.handleInput}/>;
let input = <input autoFocus id="terminal-input" style={{width: "85%"}} value={val} onChange={this.handleChange} onKeyDownCapture={this.handleInput}/>;
if (this.state.parsedInput.length > 0){
input = <div id="terminal-input" onKeyDownCapture={this.handleInput}>
const focusedInput = document.activeElement as HTMLInputElement;
input = <>
<CompletePrompt<{val: string}> value={focusedInput.value}
getData={() =>StepManager.get().getSuggestionForArg(this.state.stepRef!, focusedInput.tabIndex)}
fillIn={(val) => focusedInput.value = val.val}
render={(arg) => <span>{arg.val}</span>}
show={focusedInput !== undefined}
toggleSending={(arg) => this.setState({canSend: arg})}
keys={[{
name: "val",
weight: 1
}]}
/>
<span id="terminal-input" onKeyDownCapture={this.handleInput}>
{
this.state.parsedInput.map((val, i) =>
typeof(val) === "string" ? <span key={`const_${i}`}>{val}</span> : <input onKeyPress={(event) => {
if (event.key === "Enter"){
this.handleSubmit(document.getElementById("terminal-input") as HTMLInputElement);
}
}} key={`var_${val}`} className='arg' id={`arg-${i}`}
autoFocus={val === 0}
/>
typeof(val) === "string" ?
<span key={`const_${i}`}>{val}</span> :
<input key={`var_${val}`} className='arg' id={`arg-${val}`}
autoFocus={val === 0} tabIndex={val} onChange={() => this.forceUpdate()}
/>
)
}
</div>;
</span></>;
}
return <div style={{width: "100% "}}>
<CompletePrompt<Step> value={this.state.val}
Expand All @@ -166,6 +165,15 @@ export class TerminalInput extends Component<{}, State> {
fillIn={this.onSetStep}
getData={() => StepManager.get().getSteps()}
render={(step) => <span>{step.pattern}</span>}
keys={[
{
name: "pattern",
weight: 0.7
}, {
name: "docs",
weight: 0.3
}
]}
/>
{input}
<input type="submit"/>
Expand Down
22 changes: 0 additions & 22 deletions ui/src/interop/SuggestionManager.ts

This file was deleted.

30 changes: 30 additions & 0 deletions ui/src/interop/stepManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,36 @@ export class StepManager {
})
}

getSuggestionForArg(step:Step, i:number, stepArgs:string[] = []):Promise<{val: string}[]> | undefined{
if (step.args){
const args = step.args;

const arg = args[i];
if (arg.suggProvider !== ""){
return new Promise((resolve) => {
if (i < 0 || i >= args.length){
resolve([]);
}
else {
fetch(`${AppConfig.getServerUrl()}/suggestion`, {
method: "POST",
body: JSON.stringify({
step: step.pattern,
args: stepArgs,
argId: i
})
}).then((r => r.json())).then((suggs:string[]) => {
console.log(suggs.map((v) => ({val: v})));
resolve(suggs.map((v) => ({val: v})))
})
}
});
}
} else {
return undefined;
}
}

fetchSteps(callback:(value?:Step[]) => void | undefined){
fetch(`${AppConfig.getServerUrl()}/liststeps`).then((r) => r.json()).then((steps:Step[]) => {
this.stepRepo = this.analyzeParams(steps);
Expand Down

0 comments on commit eb6e7bb

Please sign in to comment.