-
-
Notifications
You must be signed in to change notification settings - Fork 601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: autocomplete (rebase) #4327
Open
HyperDanisH
wants to merge
15
commits into
webpack:master
Choose a base branch
from
HyperDanisH:autocomplete
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+401
−79
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
4756a1e
feat: autocomplete
HyperDanisH aaed387
refactor: moving to helpers.ts
HyperDanisH 1d210b3
chore(deps-dev): bump webpack-dev-server from 5.0.2 to 5.0.3
HyperDanisH 2b887a7
Merge pull request #1 from webpack/master
HyperDanisH 8ab2c72
chore(deps-dev) sync
HyperDanisH fbb165d
Merge pull request #2 from webpack/master
HyperDanisH 39c4a4c
refactor: moving executeAutocomplete method here to there
HyperDanisH 33047cf
refactor: removing old code from autocomplete.ts
HyperDanisH 2d5b991
feat: adding support for commands autocomplete
HyperDanisH 5b031f1
refactor: making getAutoComplete() static class function
HyperDanisH 5d15b76
fix: extra unwanted package
HyperDanisH 7f1b2ed
ci: modifying types.ts
HyperDanisH 211bd29
Delete yarn.lock
HyperDanisH 8977f48
Revert yarn.lock
HyperDanisH 4c5386b
Merge branch 'master' into autocomplete
HyperDanisH File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import * as Fs from "fs"; | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
const omelette = require("omelette"); | ||
|
||
export const appNameOnAutocomplete = "webpack-cli"; | ||
|
||
function getAutoCompleteObject() { | ||
return omelette(appNameOnAutocomplete); | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
HyperDanisH marked this conversation as resolved.
Show resolved
Hide resolved
|
||
export function setupAutoCompleteForShell(path?: string, shell?: string): any { | ||
const autoCompleteObject = getAutoCompleteObject(); | ||
let initFile = path; | ||
|
||
if (shell) { | ||
autoCompleteObject.shell = shell; | ||
} else { | ||
autoCompleteObject.shell = autoCompleteObject.getActiveShell(); | ||
} | ||
|
||
if (!initFile) { | ||
initFile = autoCompleteObject.getDefaultShellInitFile(); | ||
} | ||
|
||
let initFileContent; | ||
|
||
try { | ||
initFileContent = Fs.readFileSync(initFile as string, { encoding: "utf-8" }); | ||
} catch (exception) { | ||
throw `Can't read init file (${initFile}): ${exception}`; | ||
} | ||
|
||
try { | ||
// For bash we need to enable bash_completion before webpack cli completion | ||
if ( | ||
autoCompleteObject.shell === "bash" && | ||
initFileContent.indexOf("begin bash_completion configuration") === -1 | ||
) { | ||
const sources = `[ -f /usr/local/etc/bash_completion ] && . /usr/local/etc/bash_completion | ||
[ -f /usr/share/bash-completion/bash_completion ] && . /usr/share/bash-completion/bash_completion | ||
[ -f /etc/bash_completion ] && . /etc/bash_completion`; | ||
|
||
const template = ` | ||
# begin bash_completion configuration for ${appNameOnAutocomplete} completion | ||
${sources} | ||
# end bash_completion configuration for ${appNameOnAutocomplete} completion | ||
`; | ||
|
||
Fs.appendFileSync(initFile as string, template); | ||
} | ||
|
||
if (initFileContent.indexOf(`begin ${appNameOnAutocomplete} completion`) === -1) { | ||
autoCompleteObject.setupShellInitFile(initFile); | ||
} | ||
} catch (exception) { | ||
throw `Can't setup autocomplete. Please make sure that init file (${initFile}) exist and you have write permissions: ${exception}`; | ||
} | ||
} | ||
|
||
export function getReplyHandler( | ||
lineEndsWithWhitespaceChar: boolean, | ||
): (args: string[], autocompleteTree: IAutocompleteTree) => string[] { | ||
return function getReply(args: string[], autocompleteTree: IAutocompleteTree): string[] { | ||
const currentArg = head(args); | ||
const commandsAndCategories = Object.keys(autocompleteTree); | ||
|
||
if (currentArg === undefined) { | ||
// no more args - show all of the items at the current level | ||
return commandsAndCategories; | ||
} else { | ||
// check what arg points to | ||
const entity = autocompleteTree[currentArg]; | ||
if (entity) { | ||
// arg points to an existing command or category | ||
const restOfArgs = tail(args); | ||
if (restOfArgs.length || lineEndsWithWhitespaceChar) { | ||
if (entity instanceof Array) { | ||
// it is command | ||
const getCommandReply = getCommandReplyHandler(lineEndsWithWhitespaceChar); | ||
return getCommandReply(restOfArgs, entity); | ||
} else { | ||
// it is category | ||
return getReply(restOfArgs, entity); | ||
} | ||
} else { | ||
// if last arg has no trailing whitespace, it should be added | ||
return [currentArg]; | ||
} | ||
} else { | ||
// arg points to nothing specific - return commands and categories which start with arg | ||
return commandsAndCategories.filter((commandOrCategory) => | ||
commandOrCategory.startsWith(currentArg), | ||
); | ||
} | ||
} | ||
}; | ||
} | ||
|
||
function getCommandReplyHandler( | ||
lineEndsWithWhitespaceChar: boolean, | ||
): (args: string[], optionNames: IOptionNames[]) => string[] { | ||
return function getCommandReply(args: string[], optionsNames: IOptionNames[]): string[] { | ||
const currentArg = head(args); | ||
if (currentArg === undefined) { | ||
// no more args, returning remaining optionsNames | ||
return optionsNames.map((option) => (option.long as string) || (option.short as string)); | ||
} else { | ||
const restOfArgs = tail(args); | ||
if (restOfArgs.length || lineEndsWithWhitespaceChar) { | ||
const filteredOptions = optionsNames.filter( | ||
(option) => option.long !== currentArg && option.short !== currentArg, | ||
); | ||
return getCommandReply(restOfArgs, filteredOptions); | ||
} else { | ||
const candidates: string[] = []; | ||
for (const option of optionsNames) { | ||
if (option.long && option.long.startsWith(currentArg)) { | ||
candidates.push(option.long); | ||
} else if (option.short && option.short.startsWith(currentArg)) { | ||
candidates.push(option.short); | ||
} | ||
} | ||
return candidates; | ||
} | ||
} | ||
}; | ||
} | ||
|
||
interface IOptionNames { | ||
short?: string; | ||
long?: string; | ||
} | ||
|
||
export interface IAutocompleteTree { | ||
[entity: string]: IAutocompleteTree | IOptionNames[]; | ||
} | ||
|
||
// utility functions (to avoid loading lodash for performance reasons) | ||
|
||
function last(line: string): string { | ||
return line.substr(-1, 1); | ||
} | ||
|
||
function head<T>(array: T[]): T { | ||
return array[0]; | ||
} | ||
|
||
function tail<T>(array: T[]): T[] { | ||
return array.slice(1); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import { | ||
CommandAction, | ||
WebpackCLIBuiltInOption, | ||
WebpackCLICommand, | ||
WebpackCLICommandOptions, | ||
WebpackCLIExternalCommandInfo, | ||
WebpackCLIOptions, | ||
} from "../types"; | ||
import { IAutocompleteTree } from "./autocomplete"; | ||
|
||
const WEBPACK_PACKAGE_IS_CUSTOM = !!process.env.WEBPACK_PACKAGE; | ||
const WEBPACK_PACKAGE = WEBPACK_PACKAGE_IS_CUSTOM | ||
? (process.env.WEBPACK_PACKAGE as string) | ||
: "webpack"; | ||
|
||
export const getKnownCommands = (): WebpackCLIOptions[] => { | ||
// Built-in internal commands | ||
const buildCommandOptions = { | ||
name: "build [entries...]", | ||
alias: ["bundle", "b"], | ||
description: "Run webpack (default command, can be omitted).", | ||
usage: "[entries...] [options]", | ||
dependencies: [WEBPACK_PACKAGE], | ||
}; | ||
const watchCommandOptions = { | ||
name: "watch [entries...]", | ||
alias: "w", | ||
description: "Run webpack and watch for files changes.", | ||
usage: "[entries...] [options]", | ||
dependencies: [WEBPACK_PACKAGE], | ||
}; | ||
const versionCommandOptions = { | ||
name: "version", | ||
alias: "v", | ||
usage: "[options]", | ||
description: | ||
"Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands.", | ||
}; | ||
const setupAutocompleteCommandOptions = { | ||
name: "setup-autocomplete", | ||
alias: "a", | ||
usage: "[options]", | ||
description: "Setup tab completion for your shell", | ||
}; | ||
const helpCommandOptions = { | ||
name: "help [command] [option]", | ||
alias: "h", | ||
description: "Display help for commands and options.", | ||
}; | ||
// Built-in external commands | ||
const externalBuiltInCommandsInfo: WebpackCLIExternalCommandInfo[] = [ | ||
{ | ||
name: "serve [entries...]", | ||
alias: ["server", "s"], | ||
pkg: "@webpack-cli/serve", | ||
}, | ||
{ | ||
name: "info", | ||
alias: "i", | ||
pkg: "@webpack-cli/info", | ||
}, | ||
{ | ||
name: "init", | ||
alias: ["create", "new", "c", "n"], | ||
pkg: "@webpack-cli/generators", | ||
}, | ||
{ | ||
name: "loader", | ||
alias: "l", | ||
pkg: "@webpack-cli/generators", | ||
}, | ||
{ | ||
name: "plugin", | ||
alias: "p", | ||
pkg: "@webpack-cli/generators", | ||
}, | ||
{ | ||
name: "configtest [config-path]", | ||
alias: "t", | ||
pkg: "@webpack-cli/configtest", | ||
}, | ||
]; | ||
|
||
const knownCommands = [ | ||
buildCommandOptions, | ||
watchCommandOptions, | ||
versionCommandOptions, | ||
helpCommandOptions, | ||
setupAutocompleteCommandOptions, | ||
...externalBuiltInCommandsInfo, | ||
]; | ||
|
||
return knownCommands; | ||
}; | ||
|
||
export const getExternalBuiltInCommandsInfo = (): WebpackCLIExternalCommandInfo[] => { | ||
return [ | ||
{ | ||
name: "serve [entries...]", | ||
alias: ["server", "s"], | ||
pkg: "@webpack-cli/serve", | ||
}, | ||
{ | ||
name: "info", | ||
alias: "i", | ||
pkg: "@webpack-cli/info", | ||
}, | ||
{ | ||
name: "init", | ||
alias: ["create", "new", "c", "n"], | ||
pkg: "@webpack-cli/generators", | ||
}, | ||
{ | ||
name: "loader", | ||
alias: "l", | ||
pkg: "@webpack-cli/generators", | ||
}, | ||
{ | ||
name: "plugin", | ||
alias: "p", | ||
pkg: "@webpack-cli/generators", | ||
}, | ||
{ | ||
name: "configtest [config-path]", | ||
alias: "t", | ||
pkg: "@webpack-cli/configtest", | ||
}, | ||
]; | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs to be removed