Skip to content
This repository has been archived by the owner on May 21, 2019. It is now read-only.

Commit

Permalink
[Parser] Improve cd suggestions performance.
Browse files Browse the repository at this point in the history
  • Loading branch information
vlad-shatskyi committed Jun 1, 2016
1 parent 10d2740 commit c405c83
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 58 deletions.
19 changes: 16 additions & 3 deletions src/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,19 @@ export const choice = (parsers: Parser[]) => async (context: Context): Promise<A
return results;
};

const last = (parser: Parser): Parser => async (context: Context): Promise<Array<Result>> => {
const results = await parser(context);
return results.slice(results.length - 1);
};

const shortCircuitOnEmptyInput = (parser: Parser): Parser => async(context: Context): Promise<Array<Result>> => {
if (context.input.length === 0) {
return [];
}

return parser(context);
};

export const many1 = (parser: Parser): Parser => choice([parser, bind(parser, async () => many1(parser))]);

export const decorate = (parser: Parser, decorator: (s: Suggestion) => Suggestion) => async (context: Context): Promise<Array<Result>> => {
Expand Down Expand Up @@ -153,7 +166,7 @@ export const many = compose(many1, optional);
* @example cd ../
* @example cd -
*/
export const noisySuggestions = (parser: Parser) => decorateResult(
export const noisySuggestions = (parser: Parser) => shortCircuitOnEmptyInput(decorateResult(
parser,
result => Object.assign(
{},
Expand All @@ -162,8 +175,8 @@ export const noisySuggestions = (parser: Parser) => decorateResult(
suggestions: (result.progress !== Progress.OnStart && result.progress !== Progress.Failed) ? result.suggestions : [],
}
)
);
export const spacesWithoutSuggestion = withoutSuggestions(many1(string(" ")));
));
export const spacesWithoutSuggestion = withoutSuggestions(last(many1(string(" "))));

export const runtime = (producer: (context: Context) => Promise<Parser>) => async (context: Context): Promise<Array<Result>> => {
const parser = await producer(context);
Expand Down
36 changes: 11 additions & 25 deletions src/plugins/autocompletion_providers/Cd.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,27 @@
import {executable, sequence, decorate, string, noisySuggestions, runtime, choice} from "../../Parser";
import {directoryAlias, pathPart} from "./Common";
import {expandHistoricalDirectory} from "../../Command";
import {description, styles, style} from "./Suggestions";
import * as _ from "lodash";
import {relativeDirectoryPath} from "./File";
import {pathIn} from "./Common";

const historicalDirectory = runtime(async (context) =>
noisySuggestions(
decorate(
choice(
_.take(["-", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9"], context.historicalCurrentDirectoriesStack.length - 1)
.map(alias => decorate(string(alias), description(expandHistoricalDirectory(alias, context.historicalCurrentDirectoriesStack))))
),
style(styles.directory)
)
decorate(
choice(
_.take(["-", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9"], context.historicalCurrentDirectoriesStack.length - 1)
.map(alias => decorate(string(alias), description(expandHistoricalDirectory(alias, context.historicalCurrentDirectoriesStack))))
),
style(styles.directory)
)
);

const cdpathDirectory = runtime(
async (context) => {
const directoriesToBe = context.environment.cdpath(context.directory).map(async (directory) => {
const file = await pathPart(directory, info => info.stat.isDirectory());

if (directory === context.directory) {
return file;
} else {
return noisySuggestions(decorate(file, description(`In ${directory}`)));
}
});

return choice(await Promise.all(directoriesToBe));
}
async (context) => choice(context.environment.cdpath(context.directory).filter(directory => directory !== context.directory).map(directory =>
decorate(pathIn(directory, info => info.stat.isDirectory()), description(`In ${directory}`))))
);

export const cd = sequence(decorate(executable("cd"), description("Change the working directory")), choice([
historicalDirectory,
directoryAlias,
cdpathDirectory,
noisySuggestions(historicalDirectory),
noisySuggestions(cdpathDirectory),
relativeDirectoryPath,
]));
60 changes: 30 additions & 30 deletions src/plugins/autocompletion_providers/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,46 @@ import {statsIn, resolveDirectory} from "../../utils/Common";
import {
string,
choice,
append,
decorateResult,
decorate,
noisySuggestions,
Parser,
withoutSuggestions, runtime, many1,
withoutSuggestions, runtime, sequence,
} from "../../Parser";
import {styles, style} from "./Suggestions";
import {FileInfo} from "../../Interfaces";

const changingContextDirectory = (parser: Parser) => decorateResult(
parser,
result => Object.assign({}, result, {context: Object.assign({}, result.context, {directory: resolveDirectory(result.context.directory, result.parse)})})
);
type FileFilter = (info: FileInfo) => boolean;

export const directoryAlias = noisySuggestions(
choice([
changingContextDirectory(withoutSuggestions(string("/"))),
append("/", choice(["~", "..", "."].map(directory => changingContextDirectory(string(directory))))),
])
const pathParser = (name: string) => {
const parser = name.startsWith(".") ? noisySuggestions(string(name)) : string(name);
return decorate(parser, suggestion => suggestion.withDisplayValue(name).withValue(suggestion.value.replace(/\s/g, "\\ ")));
};
const fileParser = (info: FileInfo) => decorate(pathParser(info.name), style(styles.file(info)));
const directoryParser = (name: string) => decorate(pathParser(name), style(styles.directory));
const directoryAlias = (workingDirectory: string, filter: FileFilter) => (name: string) => sequence(
withoutSuggestions(directoryParser(name)),
pathIn(resolveDirectory(workingDirectory, name), filter)
);

const fileName = (name: string) => decorate(string(name), suggestion => suggestion.withDisplayValue(name).withValue(suggestion.value.replace(/\s/g, "\\ ")));

export const pathPart = async(directory: string, filter: (info: FileInfo) => boolean) => {
export const pathIn = (directory: string, filter: (info: FileInfo) => boolean): Parser => runtime(async() => {
const stats = await statsIn(directory);

return choice(stats.filter(filter).map(info => {
if (info.stat.isDirectory()) {
const styledDirectory = decorate(fileName(`${info.name}/`), style(styles.directory));
return changingContextDirectory(info.name.startsWith(".") ? noisySuggestions(styledDirectory) : styledDirectory);
} else {
const styled = decorate(fileName(info.name), style(styles.file(info)));
return info.name.startsWith(".") ? noisySuggestions(styled) : styled;
}
}));
};
return choice([
...stats.filter(filter).map(info => {
if (info.stat.isDirectory()) {
return sequence(
directoryParser(`${info.name}/`),
pathIn(resolveDirectory(directory, info.name), filter)
);
} else {
return fileParser(info);
}
}),
...["./", "../"].map(directoryAlias(directory, filter))
]);
});

export const pathInCurrentDirectory = (filter: (info: FileInfo) => boolean) => many1(
runtime(
async(context) => choice([directoryAlias].concat(await pathPart(context.directory, filter)))
)
);
export const pathInCurrentDirectory = (filter: FileFilter) => runtime(async(context) => choice([
...["/", "~/"].map(directoryAlias(context.directory, filter)),
pathIn(context.directory, filter),
]));

0 comments on commit c405c83

Please sign in to comment.