├── .gitignore ├── icon.png ├── example.gif ├── .vscode ├── settings.json ├── tasks.json └── launch.json ├── examples ├── example.js ├── Example.java ├── example.lua └── example.php ├── .vscodeignore ├── src ├── drivers │ ├── javascript.ts │ ├── javascriptreact.ts │ ├── typescript.ts │ ├── typescriptreact.ts │ ├── lua.ts │ ├── php.ts │ ├── java.ts │ └── abstract-javascript.ts ├── commands.ts ├── utils.ts ├── annotationProvider.ts └── extension.ts ├── tsconfig.json ├── LICENSE.md ├── README.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | .DS_STORE 6 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imliam/vscode-inline-parameters/HEAD/icon.png -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imliam/vscode-inline-parameters/HEAD/example.gif -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "out": false 4 | }, 5 | "search.exclude": { 6 | "out": true 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /examples/example.js: -------------------------------------------------------------------------------- 1 | function test(a, b, ...c) {} 2 | test('Foo', 'bar', 1, 2, 3, 4) 3 | 4 | console.log('a', 'b', 'c', 'd', 'e') 5 | 6 | console.log(message) 7 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/tslint.json 9 | **/*.map 10 | **/*.ts 11 | -------------------------------------------------------------------------------- /src/drivers/javascript.ts: -------------------------------------------------------------------------------- 1 | import { parse as abstractParse } from './abstract-javascript' 2 | export { getParameterName } from './abstract-javascript' 3 | 4 | export function parse(code: string) { 5 | return abstractParse(code, { 6 | parser: require("recast/parsers/esprima"), 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /src/drivers/javascriptreact.ts: -------------------------------------------------------------------------------- 1 | import { parse as abstractParse } from './abstract-javascript' 2 | export { getParameterName } from './abstract-javascript' 3 | 4 | export function parse(code: string) { 5 | return abstractParse(code, { 6 | parser: require("recast/parsers/babel"), 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /src/drivers/typescript.ts: -------------------------------------------------------------------------------- 1 | import { parse as abstractParse } from './abstract-javascript' 2 | export { getParameterName } from './abstract-javascript' 3 | 4 | export function parse(code: string) { 5 | return abstractParse(code, { 6 | parser: require("recast/parsers/typescript"), 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "lib": ["es6"], 6 | "outDir": "out", 7 | "sourceMap": true, 8 | "strict": false, 9 | "rootDir": "src" 10 | }, 11 | "exclude": [ 12 | "node_modules", 13 | ".vscode-test" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "watch", 7 | "problemMatcher": "$tsc-watch", 8 | "isBackground": true, 9 | "presentation": { 10 | "reveal": "never" 11 | }, 12 | "group": { 13 | "kind": "build", 14 | "isDefault": true 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /examples/Example.java: -------------------------------------------------------------------------------- 1 | package examples; 2 | 3 | 4 | public class Example { 5 | public static void test(String a, String b, int... c) { 6 | } 7 | 8 | public static void main(String[] args) { 9 | test("Foo", "bar", 1, 2, 3, 4); 10 | 11 | String message = new String(new char[]{'5', '4', '3'}); 12 | 13 | System.out.println(message); 14 | } 15 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": [ 10 | "--extensionDevelopmentPath=${workspaceFolder}" 11 | ], 12 | "outFiles": [ 13 | "${workspaceFolder}/out/**/*.js" 14 | ], 15 | "preLaunchTask": "${defaultBuildTask}" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/commands.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | 3 | export default class Commands { 4 | public static registerCommands() { 5 | vscode.commands.registerCommand('inline-parameters.toggle', () => { 6 | const currentState = vscode.workspace.getConfiguration('inline-parameters').get('enabled') 7 | 8 | vscode.workspace.getConfiguration('inline-parameters').update('enabled', !currentState, true) 9 | }) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/example.lua: -------------------------------------------------------------------------------- 1 | function AllWords () 2 | local line = io.read() 3 | local pos = 1 4 | 5 | return function () 6 | while line do 7 | local s, e = string.find(line, "%w+", pos) 8 | 9 | if s then 10 | pos = e + 1 11 | return line:sub(s, e) 12 | else 13 | line = io.read() 14 | pos = 1 15 | end 16 | end 17 | 18 | return nil 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2020 Liam Hammett 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 4 | software and associated documentation files (the "Software"), to deal in the Software 5 | without restriction, including without limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 7 | to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or 10 | substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 13 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 15 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 16 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | 3 | export interface ParameterPosition { 4 | namedValue?: string 5 | 6 | expression: { 7 | line: number, 8 | character: number, 9 | } 10 | 11 | key: number 12 | 13 | start: { 14 | line: number, 15 | character: number, 16 | } 17 | 18 | end: { 19 | line: number, 20 | character: number, 21 | } 22 | } 23 | 24 | export interface LanguageDriver { 25 | getParameterName(editor: vscode.TextEditor, position: vscode.Position, key: number, namedValue?: string): any 26 | parse(code: string): ParameterPosition[] 27 | } 28 | 29 | export function removeShebang(sourceCode: string): string { 30 | const sourceCodeArr = sourceCode.split("\n") 31 | 32 | if (sourceCodeArr[0].substr(0, 2) === "#!") { 33 | sourceCodeArr[0] = "" 34 | } 35 | 36 | return sourceCodeArr.join("\n") 37 | } 38 | 39 | export function showVariadicNumbers(str: string, number: number): string { 40 | const showVariadicNumbers = vscode.workspace.getConfiguration('inline-parameters').get('showVariadicNumbers') 41 | 42 | if (showVariadicNumbers) { 43 | return `${str}[${number}]` 44 | } 45 | 46 | return str 47 | } 48 | -------------------------------------------------------------------------------- /src/annotationProvider.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DecorationInstanceRenderOptions, 3 | ThemeColor, 4 | DecorationOptions, 5 | Range, 6 | workspace, 7 | } from "vscode" 8 | 9 | export class Annotations { 10 | public static parameterAnnotation( 11 | message: string, 12 | range: Range 13 | ): DecorationOptions { 14 | return { 15 | range, 16 | renderOptions: { 17 | before: { 18 | contentText: message, 19 | color: new ThemeColor("inlineparameters.annotationForeground"), 20 | backgroundColor: new ThemeColor("inlineparameters.annotationBackground"), 21 | fontStyle: workspace.getConfiguration("inline-parameters").get("fontStyle"), 22 | fontWeight: workspace.getConfiguration("inline-parameters").get("fontWeight"), 23 | textDecoration: `; 24 | font-size: ${workspace.getConfiguration("inline-parameters").get("fontSize")}; 25 | margin: ${workspace.getConfiguration("inline-parameters").get("margin")}; 26 | padding: ${workspace.getConfiguration("inline-parameters").get("padding")}; 27 | border-radius: ${workspace.getConfiguration("inline-parameters").get("borderRadius")}; 28 | border: ${workspace.getConfiguration("inline-parameters").get("border")}; 29 | vertical-align: middle; 30 | `, 31 | }, 32 | } as DecorationInstanceRenderOptions, 33 | } as DecorationOptions 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/drivers/typescriptreact.ts: -------------------------------------------------------------------------------- 1 | import { parse as abstractParse } from './abstract-javascript' 2 | export { getParameterName } from './abstract-javascript' 3 | 4 | export function parse(code: string) { 5 | return abstractParse(code, { 6 | parser: { 7 | parse(source: any) { 8 | const babelParser = require("recast/parsers/babel").parser 9 | 10 | const opts = { 11 | allowImportExportEverywhere: true, 12 | allowReturnOutsideFunction: true, 13 | plugins: [ 14 | "asyncGenerators", 15 | "bigInt", 16 | "classPrivateMethods", 17 | "classPrivateProperties", 18 | "classProperties", 19 | "decorators-legacy", 20 | "doExpressions", 21 | "dynamicImport", 22 | "exportDefaultFrom", 23 | "exportExtensions", 24 | "exportNamespaceFrom", 25 | "functionBind", 26 | "functionSent", 27 | "importMeta", 28 | "nullishCoalescingOperator", 29 | "numericSeparator", 30 | "objectRestSpread", 31 | "optionalCatchBinding", 32 | "optionalChaining", 33 | ["pipelineOperator", { proposal: "minimal" }], 34 | "throwExpressions", 35 | "typescript", 36 | "jsx" 37 | ], 38 | sourceType: "unambiguous", 39 | startLine: 1, 40 | strictMode: false, 41 | tokens: true 42 | } 43 | 44 | return babelParser.parse(source, opts) 45 | } 46 | } 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /examples/example.php: -------------------------------------------------------------------------------- 1 | strtoupper('Bar'), 46 | strtolower('Foo') => strtoupper('Bar'), 47 | ]; 48 | 49 | function variadic($a, $b, ...$c) {} 50 | variadic('a', 'b', 'c', 'd', 'e', 'f', 'g'); 51 | 52 | class Example 53 | { 54 | public function __construct($one) 55 | { 56 | $this->one('one'); 57 | } 58 | 59 | public function one($one) {} 60 | public function two($two) {} 61 | } 62 | 63 | $example = new Example(1); 64 | $example->two(2); 65 | 66 | if ($example === !is_null($example)) { } 67 | 68 | $ternary = is_null($example) ? strtoupper($a) : strtolower($b); 69 | 70 | $expression = 'Variables passed that match the name of the parameter name can be hidden with a setting'; 71 | var_dump($expression); 72 | -------------------------------------------------------------------------------- /src/drivers/lua.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | import { removeShebang, ParameterPosition } from '../utils' 3 | 4 | const parser = require('luaparse') 5 | 6 | export function getParameterName(editor: vscode.TextEditor, position: vscode.Position, key: number, namedValue?: string) { 7 | return new Promise(async (resolve, reject) => { 8 | let definition: string = '' 9 | let definitions: string[] 10 | const description: any = await vscode.commands.executeCommand('vscode.executeHoverProvider', editor.document.uri, position) 11 | const shouldHideRedundantAnnotations = vscode.workspace.getConfiguration('inline-parameters').get('hideRedundantAnnotations') 12 | const luaParameterNameRegex = /^[a-zA-Z_]([0-9a-zA-Z_]+)?/g 13 | 14 | if (description && description.length > 0) { 15 | try { 16 | const regEx = /^function\ .*\((.*)\)/gm 17 | definitions = description[0].contents[0].value.match(regEx) 18 | 19 | if (!definitions || !definitions[0]) { 20 | return reject() 21 | } 22 | 23 | definition = definitions[0] 24 | } catch (err) { 25 | console.error(err) 26 | } 27 | } 28 | 29 | 30 | const parameters: string[] = definition 31 | .substring(definition.indexOf('(') + 1, definition.lastIndexOf(')')) 32 | .replace(/\[/g, '').replace(/\]/g, '') 33 | .split(',') 34 | .map(parameter => parameter.trim()) 35 | .map(parameter => { 36 | const matches = parameter.match(luaParameterNameRegex) 37 | if (!matches || !matches[0]) { 38 | return null 39 | } 40 | 41 | return matches[0] 42 | }) 43 | .filter(parameter => parameter) 44 | 45 | if (!parameters || !parameters[key]) { 46 | return reject() 47 | } 48 | 49 | let name = parameters[key] 50 | 51 | if (shouldHideRedundantAnnotations && name === namedValue) { 52 | return reject() 53 | } 54 | 55 | return resolve(name) 56 | }) 57 | } 58 | 59 | export function parse(code: string): ParameterPosition[] { 60 | code = removeShebang(code) 61 | const ast: any = parser.parse(code, { 62 | comments: false, 63 | locations: true, 64 | }) 65 | const functionCalls: any[] = crawlAst(ast) 66 | let parameters: ParameterPosition[] = [] 67 | 68 | functionCalls.forEach((expression) => { 69 | parameters = getParametersFromExpression(expression, parameters) 70 | }) 71 | 72 | return parameters 73 | } 74 | 75 | function crawlAst(ast, functionCalls = []) { 76 | const canAcceptArguments = ast.type && ast.type === 'CallExpression' 77 | const hasArguments = ast.arguments && ast.arguments.length > 0 78 | const shouldHideArgumentNames = vscode.workspace.getConfiguration('inline-parameters').get('hideSingleParameters') && ast.arguments && ast.arguments.length === 1 79 | 80 | if (canAcceptArguments && hasArguments && !shouldHideArgumentNames) { 81 | functionCalls.push(ast) 82 | } 83 | 84 | for (const [key, value] of Object.entries(ast)) { 85 | if (key === 'comments') { 86 | continue 87 | } 88 | 89 | if (value instanceof Object) { 90 | functionCalls = crawlAst(value, functionCalls) 91 | } 92 | } 93 | 94 | return functionCalls 95 | } 96 | 97 | function getParametersFromExpression(expression: any, parameters: ParameterPosition[] = []): ParameterPosition[] { 98 | if (!expression.arguments) { 99 | return parameters 100 | } 101 | 102 | expression.arguments.forEach((argument: any, key: number) => { 103 | parameters.push({ 104 | namedValue: argument.name ?? null, 105 | expression: { 106 | line: parseInt(expression.base.identifier.loc.start.line) - 1, 107 | character: parseInt(expression.base.identifier.loc.start.column), 108 | }, 109 | key: key, 110 | start: { 111 | line: parseInt(argument.loc.start.line) - 1, 112 | character: parseInt(argument.loc.start.column), 113 | }, 114 | end: { 115 | line: parseInt(argument.loc.end.line) - 1, 116 | character: parseInt(argument.loc.end.column), 117 | }, 118 | }) 119 | }) 120 | 121 | return parameters 122 | } 123 | -------------------------------------------------------------------------------- /src/drivers/php.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | import { removeShebang, ParameterPosition, showVariadicNumbers } from '../utils' 3 | 4 | const engine = require("php-parser") 5 | 6 | const parser = new engine({ 7 | parser: { 8 | extractDoc: true, 9 | php7: true, 10 | locations: true, 11 | suppressErrors: true, 12 | }, 13 | ast: { 14 | all_tokens: true, 15 | withPositions: true, 16 | }, 17 | }) 18 | 19 | export function getParameterName(editor: vscode.TextEditor, position: vscode.Position, key: number, namedValue?: string) { 20 | return new Promise(async (resolve, reject) => { 21 | let isVariadic = false 22 | let parameters: any [] 23 | const description: any = await vscode.commands.executeCommand('vscode.executeHoverProvider', editor.document.uri, position) 24 | const shouldHideRedundantAnnotations = vscode.workspace.getConfiguration('inline-parameters').get('hideRedundantAnnotations') 25 | 26 | if (description && description.length > 0) { 27 | try { 28 | const regEx = /(?<=@param.+)(\.{3})?(\$[a-zA-Z0-9_]+)/g 29 | parameters = description[0].contents[0].value.match(regEx) 30 | } catch (err) { 31 | console.error(err) 32 | } 33 | } 34 | 35 | if (!parameters) { 36 | return reject() 37 | } 38 | 39 | parameters = parameters.map((parameter: any) => { 40 | if (parameter.startsWith('...')) { 41 | isVariadic = true 42 | parameter = parameter.slice(3) 43 | } 44 | 45 | return parameter 46 | }) 47 | 48 | if (isVariadic && key >= parameters.length - 1) { 49 | let name = parameters[parameters.length - 1] 50 | 51 | if (shouldHideRedundantAnnotations && name.replace('$', '') === namedValue) { 52 | return reject() 53 | } 54 | 55 | name = showDollar(name) 56 | name = showVariadicNumbers(name, -parameters.length + 1 + key) 57 | 58 | return resolve(name) 59 | } 60 | 61 | if (parameters[key]) { 62 | let name = parameters[key] 63 | 64 | if (shouldHideRedundantAnnotations && name.replace('$', '') === namedValue) { 65 | return reject() 66 | } 67 | 68 | name = showDollar(name) 69 | 70 | return resolve(name) 71 | } 72 | 73 | return reject() 74 | }) 75 | } 76 | 77 | export function parse(code: string): ParameterPosition[] { 78 | code = removeShebang(code).replace(" { 84 | parameters = getParametersFromExpression(expression, parameters) 85 | }) 86 | 87 | return parameters 88 | } 89 | 90 | function showDollar(str: string): string { 91 | if (vscode.workspace.getConfiguration('inline-parameters').get('showPhpDollar')) { 92 | return str 93 | } 94 | 95 | return str.replace('$', '') 96 | } 97 | 98 | function crawlAst(ast, functionCalls = []) { 99 | const canAcceptArguments = ast.kind && (ast.kind === 'call' || ast.kind === 'new') 100 | const hasArguments = ast.arguments && ast.arguments.length > 0 101 | const shouldHideArgumentNames = vscode.workspace.getConfiguration('inline-parameters').get('hideSingleParameters') && ast.arguments && ast.arguments.length === 1 102 | 103 | if (canAcceptArguments && hasArguments && !shouldHideArgumentNames) { 104 | functionCalls.push(ast) 105 | } 106 | 107 | for (const [key, value] of Object.entries(ast)) { 108 | if (value instanceof Object) { 109 | functionCalls = crawlAst(value, functionCalls) 110 | } 111 | } 112 | 113 | return functionCalls 114 | } 115 | 116 | function getParametersFromExpression(expression: any, parameters: ParameterPosition[] = []): ParameterPosition[] { 117 | if (!expression.arguments) { 118 | return parameters 119 | } 120 | 121 | expression.arguments.forEach((argument: any, key: number) => { 122 | if (!expression.what || (!expression.what.offset && !expression.what.loc)) { 123 | return 124 | } 125 | 126 | const expressionLoc = expression.what.offset ? expression.what.offset.loc.start : expression.what.loc.end 127 | 128 | parameters.push({ 129 | namedValue: argument.name ?? null, 130 | expression: { 131 | line: parseInt(expressionLoc.line) - 1, 132 | character: parseInt(expressionLoc.column), 133 | }, 134 | key: key, 135 | start: { 136 | line: parseInt(argument.loc.start.line) - 1, 137 | character: parseInt(argument.loc.start.column), 138 | }, 139 | end: { 140 | line: parseInt(argument.loc.end.line) - 1, 141 | character: parseInt(argument.loc.end.column), 142 | }, 143 | }) 144 | }) 145 | 146 | return parameters 147 | } 148 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | import * as phpDriver from './drivers/php' 3 | import * as luaDriver from './drivers/lua' 4 | import * as javascriptDriver from './drivers/javascript' 5 | import * as javascriptReactDriver from './drivers/javascriptreact' 6 | import * as typescriptDriver from './drivers/typescript' 7 | import * as typescriptReactDriver from './drivers/typescriptreact' 8 | import * as javaDriver from './drivers/java' 9 | import { Annotations } from './annotationProvider' 10 | import Commands from './commands' 11 | import { LanguageDriver, ParameterPosition } from './utils' 12 | 13 | const hintDecorationType = vscode.window.createTextEditorDecorationType({}) 14 | 15 | async function updateDecorations(activeEditor, languageDrivers: Record) { 16 | if (!activeEditor) { 17 | return 18 | } 19 | 20 | if (!(activeEditor.document.languageId in languageDrivers)) { 21 | return 22 | } 23 | 24 | const driver: LanguageDriver = languageDrivers[activeEditor.document.languageId] 25 | 26 | const isEnabled = vscode.workspace.getConfiguration('inline-parameters').get('enabled') 27 | 28 | if (!isEnabled) { 29 | activeEditor.setDecorations(hintDecorationType, []) 30 | return 31 | } 32 | 33 | const code = activeEditor.document.getText() 34 | let languageParameters: ParameterPosition[] = [] 35 | 36 | try { 37 | languageParameters = driver.parse(code) 38 | } catch (err) { 39 | // Error parsing language's AST, likely a syntax error on the user's side 40 | } 41 | 42 | if (languageParameters.length === 0) { 43 | return 44 | } 45 | 46 | const languageFunctions: vscode.DecorationOptions[] = [] 47 | 48 | for (let index = 0; index < languageParameters.length; index++) { 49 | var parameter = languageParameters[index] 50 | 51 | const start = new vscode.Position( 52 | parameter.start.line, 53 | parameter.start.character 54 | ) 55 | 56 | const end = new vscode.Position( 57 | parameter.end.line, 58 | parameter.end.character 59 | ) 60 | 61 | let parameterName: any 62 | 63 | try { 64 | parameterName = await driver.getParameterName( 65 | activeEditor, 66 | new vscode.Position( 67 | parameter.expression.line, 68 | parameter.expression.character 69 | ), 70 | parameter.key, 71 | parameter.namedValue 72 | ) 73 | } catch (err) { 74 | // Error getting a parameter name, just ignore it 75 | } 76 | 77 | if (!parameterName) { 78 | continue 79 | } 80 | 81 | const leadingCharacters = vscode.workspace.getConfiguration('inline-parameters').get('leadingCharacters') 82 | const trailingCharacters = vscode.workspace.getConfiguration('inline-parameters').get('trailingCharacters') 83 | const parameterCase = vscode.workspace.getConfiguration('inline-parameters').get('parameterCase') 84 | 85 | if (parameterCase === 'uppercase') { 86 | parameterName = parameterName.toUpperCase() 87 | } 88 | 89 | if (parameterCase === 'lowercase') { 90 | parameterName = parameterName.toLowerCase() 91 | } 92 | 93 | const annotation = Annotations.parameterAnnotation( 94 | leadingCharacters + parameterName + trailingCharacters, 95 | new vscode.Range(start, end) 96 | ) 97 | 98 | languageFunctions.push(annotation) 99 | } 100 | 101 | activeEditor.setDecorations(hintDecorationType, languageFunctions) 102 | } 103 | 104 | export function activate(context: vscode.ExtensionContext) { 105 | const languageDrivers: Record = { 106 | php: phpDriver, 107 | lua: luaDriver, 108 | javascript: javascriptDriver, 109 | javascriptreact: javascriptReactDriver, 110 | typescript: typescriptDriver, 111 | typescriptreact: typescriptReactDriver, 112 | java: javaDriver, 113 | } 114 | 115 | let timeout: NodeJS.Timer | undefined = undefined 116 | let activeEditor = vscode.window.activeTextEditor 117 | 118 | Commands.registerCommands() 119 | 120 | function triggerUpdateDecorations(timer: boolean = true) { 121 | if (timeout) { 122 | clearTimeout(timeout) 123 | timeout = undefined 124 | } 125 | 126 | timeout = setTimeout(() => updateDecorations(activeEditor, languageDrivers), timer ? 2500 : 25) 127 | } 128 | 129 | vscode.workspace.onDidChangeConfiguration((event) => { 130 | if (event.affectsConfiguration('inline-parameters')) { 131 | triggerUpdateDecorations(false) 132 | } 133 | }) 134 | 135 | vscode.window.onDidChangeActiveTextEditor( 136 | (editor) => { 137 | activeEditor = editor 138 | 139 | if (editor) { 140 | triggerUpdateDecorations(false) 141 | } 142 | }, 143 | null, 144 | context.subscriptions 145 | ) 146 | 147 | vscode.workspace.onDidChangeTextDocument( 148 | (event) => { 149 | if (activeEditor && event.document === activeEditor.document) { 150 | triggerUpdateDecorations(false) 151 | } 152 | }, 153 | null, 154 | context.subscriptions 155 | ) 156 | 157 | if (activeEditor) { 158 | triggerUpdateDecorations() 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/drivers/java.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | import { removeShebang, ParameterPosition, showVariadicNumbers } from '../utils' 3 | 4 | import { parse as javaparse, walk, ParseTree, JavaParserListener, MethodCallContext, ExpressionContext, ArgumentsContext } from 'java-ast' 5 | 6 | export function getParameterName(editor: vscode.TextEditor, position: vscode.Position, key: number, namedValue?: string) { 7 | return new Promise(async (resolve, reject) => { 8 | let isVariadic = false 9 | let parameters: any[] 10 | const description: any = await vscode.commands.executeCommand('vscode.executeHoverProvider', editor.document.uri, position) 11 | const shouldHideRedundantAnnotations = vscode.workspace.getConfiguration('inline-parameters').get('hideRedundantAnnotations') 12 | 13 | if (description && description.length > 0) { 14 | try { 15 | const functionDefinitionRegex = /[^ ](?!^)\((.*)\)/gm 16 | let definition = description[0].contents[0].value.match(functionDefinitionRegex) 17 | 18 | if (!definition || definition.length === 0) { 19 | return reject() 20 | } 21 | 22 | definition = definition[0].slice(2, -1) 23 | 24 | const jsParameterNameRegex = /[a-zA-Z_$][0-9a-zA-Z_$]*$/g 25 | 26 | parameters = definition.split(',') 27 | .map((parameter: string) => parameter.trim()) 28 | .map((parameter: string) => { 29 | if (parameter.includes('...')) { 30 | isVariadic = true 31 | } 32 | 33 | const matches = parameter.match(jsParameterNameRegex) 34 | 35 | if (matches && matches.length) { 36 | return matches[0] 37 | } 38 | 39 | return parameter 40 | }) 41 | } catch (err) { 42 | console.error(err) 43 | } 44 | } 45 | 46 | if (!parameters) { 47 | return reject() 48 | } 49 | 50 | if (isVariadic && key >= parameters.length - 1) { 51 | let name = parameters[parameters.length - 1] 52 | 53 | if (shouldHideRedundantAnnotations && name === namedValue) { 54 | return reject() 55 | } 56 | 57 | name = showVariadicNumbers(name, -parameters.length + 1 + key) 58 | 59 | return resolve(name) 60 | } 61 | 62 | if (parameters[key]) { 63 | let name = parameters[key] 64 | 65 | if (shouldHideRedundantAnnotations && name === namedValue) { 66 | return reject() 67 | } 68 | 69 | return resolve(name) 70 | } 71 | 72 | return reject() 73 | }) 74 | } 75 | 76 | export function parse(code: string): ParameterPosition[] { 77 | code = removeShebang(code) 78 | const ast = javaparse(code) 79 | const editor = vscode.window.activeTextEditor 80 | 81 | const functionCalls: any[] = getFunctionCalls(ast) 82 | let parameters: ParameterPosition[] = [] 83 | 84 | for (const call of functionCalls) { 85 | parameters = getParametersFromMethod(editor, call, parameters) 86 | } 87 | 88 | return parameters 89 | } 90 | 91 | function getFunctionCalls(ast: ParseTree): any[] { 92 | let functionCalls: any[] = [] 93 | 94 | const hideSingleParameters = vscode.workspace.getConfiguration('inline-parameters').get('hideSingleParameters') 95 | 96 | class JavaMethodListener implements JavaParserListener { 97 | enterArguments = (args: ArgumentsContext) => { 98 | const params = args.expressionList().expression() 99 | if (!(hideSingleParameters && params.length === 1)) { 100 | functionCalls.push(args); 101 | } 102 | } 103 | enterMethodCall = (method: MethodCallContext) => { 104 | const params = method.expressionList().expression() 105 | if (!(hideSingleParameters && params.length === 1)) { 106 | functionCalls.push(method); 107 | } 108 | }; 109 | } 110 | 111 | const listener: JavaParserListener = new JavaMethodListener() 112 | 113 | walk(listener, ast) 114 | 115 | return functionCalls 116 | } 117 | 118 | function position(parameter) { 119 | const start = parameter.start.line + "L" + parameter.start.character + "C" 120 | const end = parameter.end.line + "L" + parameter.end.character + "C" 121 | const exp = parameter.expression.line + "L" + parameter.expression.character + "C" 122 | return "[" + start + " - " + end + "] @ " + exp 123 | } 124 | 125 | function getParametersFromMethod(editor: vscode.TextEditor, method: any, parameters: ParameterPosition[]): ParameterPosition[] { 126 | 127 | let params = method.expressionList().expression() 128 | 129 | params.forEach((param, key) => { 130 | parameters.push(parseParam(editor, method, param, key)) 131 | }) 132 | 133 | return parameters 134 | } 135 | 136 | function parseParam(editor: vscode.TextEditor, expression: any, argument: ExpressionContext, key: number): ParameterPosition { 137 | const parameter: ParameterPosition = { 138 | namedValue: argument.text ?? null, 139 | expression: { 140 | line: expression.start.line - 1, 141 | character: expression.start.charPositionInLine, 142 | }, 143 | key: key, 144 | start: { 145 | line: argument.start.line - 1, 146 | character: argument.start.charPositionInLine, 147 | }, 148 | end: { 149 | line: argument.stop.line - 1, 150 | character: argument.stop.charPositionInLine, 151 | }, 152 | } 153 | 154 | const line = editor.document.lineAt(parameter.start.line) 155 | 156 | const offset = editor.options.insertSpaces ? 0 : line.firstNonWhitespaceCharacterIndex * 3 157 | 158 | parameter.expression.character -= offset 159 | parameter.start.character -= offset 160 | parameter.end.character -= offset 161 | 162 | console.log("JACK: " + argument.text + position(parameter)) 163 | 164 | return parameter 165 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Inline Parameters for VSCode 2 | 3 |

4 | Inline Parameters for VSCode 5 |

6 | 7 |

8 | VS Marketplace Version 9 | VS Marketplace Installs 10 | VS Marketplace Rating 11 |

12 | 13 |

14 | An extension for Visual Studio Code that adds inline parameter annotations when calling a function. 15 |

16 | 17 |

18 | Example of extension 19 |

20 | 21 | This is a feature that was [popularised by JetBrains' IDEs](https://blog.jetbrains.com/phpstorm/2017/03/new-in-phpstorm-2017-1-parameter-hints/) that can give you additional context when reading your code, making it easier to understand what different function parameters refer to by showing the parameter's name inline. 22 | 23 | No longer do you have to be confused about whether the needle or haystack comes first, or have to slow down your workflow by going to a function's source to figure out what it does! 24 | 25 | ## Language Support 26 | 27 | Currently, this extension supports the following languages: 28 | 29 | - JavaScript (and with React) 30 | - TypeScript (and with React) 31 | - PHP (with the [Intelephense](https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client) language server) 32 | - Lua (with [Sumneko's Lua language server](https://marketplace.visualstudio.com/items?itemName=sumneko.lua)) 33 | 34 | ### Want to contribute additional language support? 35 | 36 | Additional language support is welcome as pull requests, and highly encouraged. You can see the source code to see how existing languages have been implemented. 37 | 38 | Currently, the extension has 2 major steps that all language drivers must implement: 39 | 40 | 1. Parsing the source code of the currently active file (eg. by using an AST library - [AST Explorer](https://astexplorer.net/) can assist in navigating it) to retrieve a list of positions where annotations should be inserted 41 | 2. Getting the name of the parameters to use as the annotations. Existing language drivers does this by triggering the hover providers for the function being called, and extracting the parameter names from the description 42 | 43 | ## Settings 44 | 45 | The extension provides a handful of configuration settings you can use to customise the look and behaviour of the parameters. 46 | 47 | | Name | Description | Default | 48 | |-------|------------|---------| 49 | | `inline-parameters.enabled` | Show inline parameters | `true` | 50 | | `inline-parameters.leadingCharacters` | Characters to be shown before each parameter annotation | `""` | 51 | | `inline-parameters.trailingCharacters` | Characters to be shown after each parameter annotation | `":"` | 52 | | `inline-parameters.showPhpDollar` | Show the $ character before PHP parameter names | `false` | 53 | | `inline-parameters.hideSingleParameters` | Hide inline parameters if a function only has 1 parameter | `false` | 54 | | `inline-parameters.parameterCase` | Forcibly change the case of the inline parameter name. Options are `normal`, `lowercase` or `uppercase` | `"normal"` | 55 | | `inline-parameters.showVariadicNumbers` | Show the number of times a variadic parameter has been called | `true` | 56 | | `inline-parameters.hideRedundantAnnotations` | If the value given to a parameter is the same as the parameter name, hide the parameter name | `true` | 57 | 58 | There are also a handful of settings that can be used to customise the styling of the annotation to your own personal preference. 59 | 60 | | Name | Description | Default | 61 | |-------|------------|---------| 62 | | `inline-parameters.fontWeight` | Annotation styling of font-weight CSS property | `"400"` | 63 | | `inline-parameters.fontStyle` | Annotation styling of font-style CSS property | `"italic"` | 64 | | `inline-parameters.fontSize` | Annotation styling of font size CSS property | `0.85em` | 65 | | `inline-parameters.margin` | Annotation styling of margin CSS property | `0.25em` | 66 | | `inline-parameters.padding` | Annotation styling of padding CSS property | `0.25em 0.5em` | 67 | | `inline-parameters.border` | Annotation styling of border CSS property | `none` | 68 | | `inline-parameters.borderRadius` | Annotation styling of border-radius CSS property | `0.25em` | 69 | 70 | ## Themable Colours 71 | 72 | You can change the default foreground and background colours in the `workbench.colorCustomizations` property in user settings. 73 | 74 | | Name | Description | 75 | |------|-------------| 76 | | `inlineparameters.annotationForeground` | Specifies the foreground colour for the annotations | 77 | | `inlineparameters.annotationBackground` | Specifies the background colour for the annotations | 78 | 79 | ## Commands 80 | 81 | | Name | Description | Keyboard Shortcut | 82 | |------|-------------|-------------------| 83 | | `inline-parameters.toggle` | Hide / show inline parameters | Ctrl K A or ⌘ K A | 84 | 85 | ## Credits / Links 86 | 87 | - [Liam Hammett](https://github.com/imliam) 88 | - [VSCode's Extension Samples](https://github.com/microsoft/vscode-extension-samples/tree/master/decorator-sample), which was a huge help to get started 89 | - [Benjamin Lannon](https://github.com/lannonbr) for the (no longer maintained) [VSCode JS Annotations extension](https://github.com/lannonbr/vscode-js-annotations) (where some AST parsing for the Javascript languages was borrowed from) 90 | - [Bobby Zrncev](https://github.com/bzrncev) for the [IntelliJ Parameter Hints](https://github.com/bzrncev/intellij-parameter-hints) extension which achieves the same for PHP 91 | - [All Contributors](../../contributors) 92 | 93 | ## License 94 | 95 | The MIT License (MIT). Please see the [license file](LICENSE.md) for more information. 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inline-parameters", 3 | "displayName": "Inline Parameters for VSCode", 4 | "description": "Function parameter annotations displaying inline in VSCode", 5 | "publisher": "liamhammett", 6 | "icon": "icon.png", 7 | "version": "0.2.1", 8 | "license": "MIT", 9 | "homepage": "https://github.com/imliam/vscode-inline-parameters", 10 | "bugs": { 11 | "url": "https://github.com/imliam/vscode-inline-parameters/issues" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/imliam/vscode-inline-parameters" 16 | }, 17 | "main": "./out/extension.js", 18 | "engines": { 19 | "vscode": "^1.40.0" 20 | }, 21 | "categories": [ 22 | "Other" 23 | ], 24 | "activationEvents": [ 25 | "*" 26 | ], 27 | "contributes": { 28 | "commands": [ 29 | { 30 | "command": "inline-parameters.toggle", 31 | "title": "Inline Parameters: Hide / Show" 32 | } 33 | ], 34 | "configuration": [ 35 | { 36 | "title": "Inline Parameters", 37 | "properties": { 38 | "inline-parameters.enabled": { 39 | "type": "boolean", 40 | "description": "Show inline parameters", 41 | "default": true 42 | }, 43 | "inline-parameters.leadingCharacters": { 44 | "type": "string", 45 | "description": "Characters to be shown before each parameter annotation", 46 | "default": "" 47 | }, 48 | "inline-parameters.trailingCharacters": { 49 | "type": "string", 50 | "description": "Characters to be shown after each parameter annotation", 51 | "default": ":" 52 | }, 53 | "inline-parameters.showPhpDollar": { 54 | "type": "boolean", 55 | "description": "Show the $ character before PHP parameter names", 56 | "default": false 57 | }, 58 | "inline-parameters.hideSingleParameters": { 59 | "type": "boolean", 60 | "description": "Hide inline parameters if a function only has 1 parameter", 61 | "default": false 62 | }, 63 | "inline-parameters.parameterCase": { 64 | "type": "string", 65 | "description": "Forcibly change the case of the inline parameter name", 66 | "enum": [ 67 | "normal", 68 | "lowercase", 69 | "uppercase" 70 | ], 71 | "default": "normal" 72 | }, 73 | "inline-parameters.showVariadicNumbers": { 74 | "type": "boolean", 75 | "description": "Show the number of times a variadic parameter has been called", 76 | "default": true 77 | }, 78 | "inline-parameters.hideRedundantAnnotations": { 79 | "type": "boolean", 80 | "description": "If the value given to a parameter is the same as the parameter name, hide it", 81 | "default": true 82 | }, 83 | "inline-parameters.fontWeight": { 84 | "type": "string", 85 | "default": "400", 86 | "enum": [ 87 | "100", 88 | "200", 89 | "300", 90 | "400", 91 | "500", 92 | "600", 93 | "700", 94 | "800", 95 | "900" 96 | ], 97 | "description": "Annotation styling of font-weight CSS property" 98 | }, 99 | "inline-parameters.fontStyle": { 100 | "type": "string", 101 | "default": "italic", 102 | "enum": [ 103 | "normal", 104 | "italic" 105 | ], 106 | "description": "Annotation styling of font-style CSS property" 107 | }, 108 | "inline-parameters.fontSize": { 109 | "type": "string", 110 | "default": "0.85em", 111 | "description": "Annotation styling of font size CSS property" 112 | }, 113 | "inline-parameters.margin": { 114 | "type": "string", 115 | "default": "0.25em", 116 | "description": "Annotation styling of margin CSS property" 117 | }, 118 | "inline-parameters.padding": { 119 | "type": "string", 120 | "default": "0.25em 0.5em", 121 | "description": "Annotation styling of padding CSS property" 122 | }, 123 | "inline-parameters.borderRadius": { 124 | "type": "string", 125 | "default": "0.25em", 126 | "description": "Annotation styling of border-radius CSS property" 127 | }, 128 | "inline-parameters.border": { 129 | "type": "string", 130 | "default": "none", 131 | "description": "Annotation styling of border CSS property" 132 | } 133 | } 134 | } 135 | ], 136 | "colors": [ 137 | { 138 | "id": "inlineparameters.annotationForeground", 139 | "description": "Specifies the foreground color for the annotations", 140 | "defaults": { 141 | "dark": "#adbec5", 142 | "light": "#797a79", 143 | "highContrast": "#adbec5" 144 | } 145 | }, 146 | { 147 | "id": "inlineparameters.annotationBackground", 148 | "description": "Specifies the background color for the annotations", 149 | "defaults": { 150 | "dark": "#1e2c31", 151 | "light": "#f4f5f4", 152 | "highContrast": "#1e2c31" 153 | } 154 | } 155 | ], 156 | "keybindings": [ 157 | { 158 | "command": "inline-parameters.toggle", 159 | "key": "ctrl+k a", 160 | "mac": "cmd+k a", 161 | "when": "editorFocus" 162 | } 163 | ] 164 | }, 165 | "scripts": { 166 | "preinstall": "mkdir -p ./node_modules && touch ./node_modules/.metadata_never_index", 167 | "vscode:prepublish": "npm run compile", 168 | "compile": "tsc -p ./", 169 | "watch": "tsc -watch -p ./" 170 | }, 171 | "devDependencies": { 172 | "@types/glob": "^7.1.2", 173 | "@types/mocha": "^5.0.0", 174 | "@types/node": "^12.12.44", 175 | "@types/vscode": "^1.40.0", 176 | "glob": "^7.1.5", 177 | "mocha": "^6.2.2", 178 | "typescript": "^3.9.5", 179 | "vscode-test": "^1.2.2" 180 | }, 181 | "dependencies": { 182 | "babylon": "^7.0.0-beta.47", 183 | "install": "^0.13.0", 184 | "java-ast": "^0.3.0", 185 | "luaparse": "^0.3.0", 186 | "npm": "^6.14.5", 187 | "php-parser": "^3.0.0", 188 | "recast": "^0.16.0" 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/drivers/abstract-javascript.ts: -------------------------------------------------------------------------------- 1 | import * as recast from "recast" 2 | import * as vscode from 'vscode' 3 | import { removeShebang, ParameterPosition, showVariadicNumbers } from "../utils" 4 | 5 | export function getParameterName(editor: vscode.TextEditor, position: vscode.Position, key: number, namedValue?: string) { 6 | return new Promise(async (resolve, reject) => { 7 | let isVariadic = false 8 | let parameters: any[] 9 | const description: any = await vscode.commands.executeCommand('vscode.executeHoverProvider', editor.document.uri, position) 10 | const shouldHideRedundantAnnotations = vscode.workspace.getConfiguration('inline-parameters').get('hideRedundantAnnotations') 11 | 12 | if (description && description.length > 0) { 13 | try { 14 | const functionDefinitionRegex = /[^ ](?!^)\((.*)\)\:/gm 15 | let definition = description[0].contents[0].value.match(functionDefinitionRegex) 16 | 17 | if (!definition || definition.length === 0) { 18 | return reject() 19 | } 20 | 21 | definition = definition[0].slice(2, -2) 22 | 23 | const jsParameterNameRegex = /^[a-zA-Z_$]([0-9a-zA-Z_$]+)?/g 24 | 25 | parameters = definition.split(',') 26 | .map((parameter: any) => parameter.trim()) 27 | .map((parameter: any) => { 28 | if (parameter.startsWith('...')) { 29 | isVariadic = true 30 | parameter = parameter.slice(3) 31 | } 32 | 33 | const matches = parameter.match(jsParameterNameRegex) 34 | 35 | if (matches && matches.length) { 36 | return matches[0] 37 | } 38 | 39 | return parameter 40 | }) 41 | } catch (err) { 42 | console.error(err) 43 | } 44 | } 45 | 46 | if (!parameters) { 47 | return reject() 48 | } 49 | 50 | if (isVariadic && key >= parameters.length - 1) { 51 | let name = parameters[parameters.length - 1] 52 | 53 | if (shouldHideRedundantAnnotations && name === namedValue) { 54 | return reject() 55 | } 56 | 57 | name = showVariadicNumbers(name, -parameters.length + 1 + key) 58 | 59 | return resolve(name) 60 | } 61 | 62 | if (parameters[key]) { 63 | let name = parameters[key] 64 | 65 | if (shouldHideRedundantAnnotations && name === namedValue) { 66 | return reject() 67 | } 68 | 69 | return resolve(name) 70 | } 71 | 72 | return reject() 73 | }) 74 | } 75 | 76 | export function parse(code: string, options: any) { 77 | code = removeShebang(code) 78 | let javascriptAst: any = '' 79 | let parameters: ParameterPosition[] = [] 80 | const editor = vscode.window.activeTextEditor 81 | 82 | try { 83 | javascriptAst = recast.parse(code, options).program.body 84 | } catch (err) { 85 | return parameters 86 | } 87 | 88 | parameters = lookForFunctionCalls(editor, parameters, javascriptAst) 89 | 90 | return parameters 91 | } 92 | 93 | function lookForFunctionCalls(editor: vscode.TextEditor, parameters: ParameterPosition[], body: any): ParameterPosition[] { 94 | let arr = [] 95 | 96 | function getNodes(astNode, nodeArr) { 97 | // Loop through all keys in the current node 98 | for (const key in astNode) { 99 | if (astNode.hasOwnProperty(key)) { 100 | const item = astNode[key] 101 | 102 | if (item === undefined || item === null) { 103 | continue 104 | } 105 | 106 | if (Array.isArray(item)) { 107 | // If the current node is an array of nodes, loop through each 108 | item.forEach((subItem) => nodeArr = getNodes(subItem, nodeArr)) 109 | } else if (item.loc !== undefined) { 110 | // If is a proper node and has a location in the source, push it into the array and recurse on that for nodes inside this node 111 | nodeArr.push(item) 112 | nodeArr = getNodes(item, nodeArr) 113 | } 114 | } 115 | } 116 | 117 | return nodeArr 118 | } 119 | 120 | arr = getNodes(body, arr) 121 | 122 | const nodes = arr.filter((node) => node.type === "CallExpression" || node.type === "NewExpression") 123 | 124 | const calls = [] 125 | 126 | nodes.forEach((node) => { 127 | if (node.type === "NewExpression") { 128 | calls.push(node, ...node.arguments) 129 | } else { 130 | calls.push(node) 131 | } 132 | }) 133 | 134 | for (const call of calls) { 135 | if (call.callee && call.callee.loc) { 136 | 137 | if (call.arguments) { 138 | const hideSingleParameters = vscode.workspace.getConfiguration('inline-parameters').get('hideSingleParameters') 139 | 140 | if (hideSingleParameters && call.arguments.length === 1) { 141 | continue 142 | } 143 | 144 | const expression = getExpressionLoc(call) 145 | 146 | call.arguments.forEach((argument: any, key: number) => { 147 | parameters.push(parseParam(argument, key, expression, editor)) 148 | }) 149 | } 150 | } 151 | } 152 | 153 | return parameters 154 | } 155 | 156 | function parseParam(argument: any, key: number, expression: any, editor: vscode.TextEditor): ParameterPosition { 157 | const parameter: ParameterPosition = { 158 | namedValue: argument.name ?? null, 159 | expression: { 160 | line: expression.start.line, 161 | character: expression.start.column, 162 | }, 163 | key: key, 164 | start: { 165 | line: argument.loc.start.line - 1, 166 | character: argument.loc.start.column, 167 | }, 168 | end: { 169 | line: argument.loc.end.line - 1, 170 | character: argument.loc.end.column, 171 | }, 172 | } 173 | 174 | // TSTypeAssertions are off by one for some reason so subtract the column by one. 175 | if (argument.type === "TSTypeAssertion") { 176 | parameter.start.character -= 1 177 | } 178 | 179 | const line = editor.document.lineAt(parameter.start.line) 180 | 181 | const offset = editor.options.insertSpaces ? 0 : line.firstNonWhitespaceCharacterIndex * 3 182 | 183 | parameter.expression.character -= offset 184 | parameter.start.character -= offset 185 | parameter.end.character -= offset 186 | 187 | return parameter 188 | } 189 | 190 | function getExpressionLoc(call: any) { 191 | if (call.callee.type === "MemberExpression" && call.callee.property.loc) { 192 | const { start, end } = call.callee.property.loc 193 | 194 | return { 195 | start: { 196 | line: start.line - 1, 197 | column: start.column 198 | }, 199 | end: { 200 | line: end.line - 1, 201 | column: end.column 202 | } 203 | } 204 | } 205 | 206 | if (call.callee.type === "CallExpression") { 207 | const { start, end } = call.callee.arguments[0].loc 208 | 209 | return { 210 | start: { 211 | line: start.line - 1, 212 | column: start.column 213 | }, 214 | end: { 215 | line: end.line - 1, 216 | column: end.column 217 | } 218 | } 219 | } 220 | 221 | const { start, end } = call.callee.loc 222 | 223 | return { 224 | start: { 225 | line: start.line - 1, 226 | column: start.column 227 | }, 228 | end: { 229 | line: end.line - 1, 230 | column: end.column 231 | } 232 | } 233 | } 234 | --------------------------------------------------------------------------------