-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1357 from sasjs/snippet
Snippets generation feature
- Loading branch information
Showing
19 changed files
with
609 additions
and
13 deletions.
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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,118 @@ | ||
import { | ||
listSasFilesInFolder, | ||
asyncForEach, | ||
readFile, | ||
createFile, | ||
isTestFile, | ||
getLineEnding | ||
} from '@sasjs/utils' | ||
import path from 'path' | ||
|
||
interface Snippet { | ||
prefix: string | ||
body: string | ||
description: string[] | ||
} | ||
|
||
const sasRegExp = /\.sas$/ // to check if file has .sas extension | ||
const briefRegExp = /^\s\s@brief\s/ // to get lines that has @brief keyword | ||
const paramRegExp = /^\s\s@param\s/ // to get lines that has @param keyword | ||
|
||
/** | ||
* Generates VS Code snippets from the Doxygen headers in the SAS Macros. | ||
* @param macroFolders an array of file paths of SAS Macros. | ||
* @param outDirectory a folder path where generated snippets should be put into. If ends with '<file name>.json' then provided file name will be used, otherwise default file name ('sasjs-macro-snippets.json') will be used. | ||
* @returns a promise that resolves into a file path with generated VS Code snippets. | ||
*/ | ||
export async function generateSnippets( | ||
macroFolders: string[], | ||
outDirectory?: string | ||
) { | ||
// an object that represents generated snippets | ||
const snippets: { [key: string]: Snippet } = {} | ||
|
||
// return an error if no macro folders has been provided | ||
if (!macroFolders.length) { | ||
return Promise.reject( | ||
`"macroFolders" array was not found in sasjs/sasjsconfig.json.` | ||
) | ||
} | ||
|
||
// generate snippets from all .sas file in macro folders | ||
await asyncForEach(macroFolders, async (folder) => { | ||
// get .sas files excluding test('*.test.sas') files | ||
const sasFiles = (await listSasFilesInFolder(folder, true)) | ||
.map((file) => path.join(folder, file)) | ||
.filter((file) => !isTestFile(file)) | ||
|
||
await asyncForEach(sasFiles, async (file) => { | ||
// get macro name | ||
const macro: string = file.split(path.sep).pop().replace(sasRegExp, '') | ||
|
||
// put generated snippet into snippets object | ||
snippets[macro] = await createSnippetFromMacro(file) | ||
}) | ||
}) | ||
|
||
// return an error if no snippets has been generated | ||
if (!Object.keys(snippets).length) { | ||
return Promise.reject('No VS Code snippets has been found.') | ||
} | ||
|
||
const defaultOutputFileName = 'sasjs-macro-snippets.json' | ||
const { buildDestinationResultsFolder } = process.sasjsConstants | ||
|
||
// if outDirectory is provided, use it depending if it has file name or a folder only. If outDirectory is not provided, default file name and build result folder should be used. | ||
const snippetsFilePath = path.join( | ||
outDirectory ? process.projectDir : buildDestinationResultsFolder, | ||
outDirectory | ||
? /\.json$/.test(outDirectory) | ||
? outDirectory | ||
: path.join(outDirectory, defaultOutputFileName) | ||
: defaultOutputFileName | ||
) | ||
|
||
// create file with generated VS Code snippets | ||
await createFile(snippetsFilePath, JSON.stringify(snippets, null, 2)) | ||
|
||
// return file path with generated VS Code snippets | ||
return Promise.resolve(snippetsFilePath) | ||
} | ||
|
||
/** | ||
* Creates a VS Code snippet from SAS macro. | ||
* @param file file path of SAS macro. | ||
* @returns promise that resolves with VS Code snippet. | ||
*/ | ||
const createSnippetFromMacro = async (file: string): Promise<Snippet> => { | ||
const fileContent = await readFile(file) | ||
const lineEnding = getLineEnding(fileContent) // LF or CRLF | ||
const lines = fileContent.split(lineEnding) | ||
|
||
let brief = lines.filter((line) => briefRegExp.test(line)) | ||
let params = lines.filter((line) => paramRegExp.test(line)) | ||
const macro: string = file.split(path.sep).pop()!.replace(sasRegExp, '') // macro name | ||
|
||
// if brief present, remove @brief keyword | ||
if (brief.length) brief = [brief[0].replace(briefRegExp, '')] | ||
|
||
// if params present, add a line break before list of params and a prefix to each param | ||
if (params.length) { | ||
brief.push('\r') | ||
|
||
params = params.map((param) => param.replace(paramRegExp, '-')) | ||
} | ||
|
||
// construct snippet description removing empty lines | ||
const description = [ | ||
...brief, | ||
params.length ? `Params:` : '', | ||
...params | ||
].filter((line) => line) | ||
|
||
return { | ||
prefix: `%${macro}`, | ||
body: `%${macro}($1)`, | ||
description: description | ||
} | ||
} |
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,62 @@ | ||
import { generateSnippets } from './snippets' | ||
import { CommandExample, ReturnCode } from '../../types/command' | ||
import { TargetCommand } from '../../types/command/targetCommand' | ||
import { getMacroFolders } from '../../utils' | ||
|
||
const syntax = 'snippets' | ||
const usage = 'sasjs snippets' | ||
const description = `Generates VS Code snippets from the Doxygen headers in the SAS Macros.` | ||
const examples: CommandExample[] = [ | ||
{ | ||
command: 'sasjs snippets', | ||
description: description | ||
} | ||
] | ||
|
||
const parseOptions = { | ||
outDirectory: { | ||
type: 'string', | ||
alias: 'o', | ||
description: | ||
'Path to the directory where the VS Code snippets output will be generated.' | ||
} | ||
} | ||
|
||
/** | ||
* 'snippets' command class. | ||
*/ | ||
export class SnippetsCommand extends TargetCommand { | ||
constructor(args: string[]) { | ||
super(args, { syntax, usage, description, examples, parseOptions }) | ||
} | ||
|
||
/** | ||
* Command execution method. | ||
* @returns promise that results into return code. | ||
*/ | ||
public async execute() { | ||
const { target } = await this.getTargetInfo() | ||
const macroFolders = await getMacroFolders(target) | ||
const { outDirectory } = this.parsed | ||
|
||
// Generate snippets | ||
return await generateSnippets(macroFolders, outDirectory as string) | ||
.then((filePath) => { | ||
// handle command execution success | ||
process.logger?.success( | ||
`VS Code snippets generated! File location: ${filePath}` | ||
) | ||
process.logger?.info( | ||
`Follow these instructions https://cli.sasjs.io/snippets/#import-snippets-to-vs-code to import generated VS Code snippets into your VS Code.` | ||
) | ||
|
||
return ReturnCode.Success | ||
}) | ||
.catch((err) => { | ||
// handle command execution failure | ||
process.logger?.error('Error generating VS Code snippets: ', err) | ||
|
||
return ReturnCode.InternalError | ||
}) | ||
} | ||
} |
Empty file.
Oops, something went wrong.