Code Monkey home page Code Monkey logo

js-assistant's Introduction

The P42 JS Assistant adds over 120 code actions and a suggestion panel for JavaScript, TypeScript, React, and Vue.js to Visual Studio Code.

@p42ai makes refactoring a ton of fun ❤️   — John Reilly

Give it a try, it's awesome!  — Kasper Mikiewicz

Documentation

You can find the actions in the quick fix and refactoring context menus. They depend on the cursor position, the selected text (if any), the source code, the language type, and any available type information. Underlining with three dots suggests beneficial refactorings that you can perform. The suggestion panel shows you recommended refactorings for your whole file.

Keyboard Shortcuts

Action Type Mac Shortcut Windows/Linux Shortcut
Quick Fix context menu + . Ctrl + .
Refactor context menu + + R Ctrl + Alt + R
Source Action context menu + + S Ctrl + Alt + S
Extract direct + + X Ctrl + Alt + X
Inline direct + + I Ctrl + Alt + I
Toggle Braces {} direct + + B Ctrl + Alt + B
Move Up direct + + Ctrl + Alt + U
Move Down direct + + Ctrl + Alt + J
Other Action context menu + + A Ctrl + Alt + A

Touch Bar

The P42 JS Assistant adds the following buttons to the MacOS touch bar:

  • ✏️   Rename: Triggers the rename command on the current symbol.
  • 🪄   Quick Fix: Opens the quick-fix context menu.
  • 🔧   Refactor: Opens the refactor context menu.
  • 📄   Source Action: Opens the source action context menu.

Code Assists by Category

Code assists that belong to several categories appear more than once.

Core Refactorings

Visual Study Code already contains basic refactorings such as Rename and Extract Function. The JS Assistant adds additional refactorings or extended functionality such as safety checking:

  • Extract selected text into variable: Extract the selected text (including expressions from template literals) into a const variable.
  • Extract variable: Extract one or more occurrences of an expression into a const variable.
  • Replace assignment with return: Convert a variable assignment to a return statement.
  • Inline variable: Inline a variable value into its references.

Code Assists for Logical Expressions

Boolean logic can be challenging to read, especially as expressions get more complex. The JS Assistant provides several refactorings that can help you simplify and tease apart logical expressions to make them easier to understand:

  • Convert string comparison chain to array.includes(): Replace || value === 'aString' and && value !== 'aString' chains with array.includes().
  • Convert to optional chaining: Replace various guard expressions with the optional chaining operator (?.).
  • Flip operator: Swap the left and right operands and update the operator if necessary.
  • Invert condition: Negate the condition of an if-statement or conditional expression and swap its content.
  • Pull up negation: Move the not-operator (!) out of a binary expression.
  • Push down negation: Push the not-operator (!) into an expression and negate it.
  • Remove double negation: Remove double negation (!!) expressions.
  • Simplify binary expression: Replace binary expression with a more straightforward equivalent expression.
  • Use == null comparison: Replace different nullish checks with == null.

Code Assists for Branching Statements

Branching statements such as if-else and switch are central elements in many programs. Restructuring them can increase the readability of your programs, often in combination with refactoring their conditions:

  • Add {…} to if-else and loops: Convert single statements into blocks.
  • Convert && to if statement: Convert condition && aFunction(); and similar expression statements into if statements.
  • Convert conditional expression to if-else: Convert a conditional expression to an if-else statement.
  • Convert if-else into conditional expression: Convert an if-else return or assignment expression into a conditional expression.
  • Convert if-else to switch: Convert a series of if-else statements with equality comparisons into a switch statement.
  • Convert switch to if-else: Change a switch statement into a series of if-else statements
  • Inline variable occurrence: Inline the value of a variable into one of its occurrences.
  • Introduce early return / continue: Change an if-statement into an early return or continue statement.
  • Merge if-statement into preceding if-statement: Merges two if-statements with the same body when possible.
  • Merge nested if inside else into else-if: Nested single if statements inside else blocks can be combined into else if statements.
  • Merge nested if-statements: Combine two nested if statements without additional operations into a single if-statement, using && to combine the conditions.
  • Move duplicated first statement out of if-else: Move a first statement that appears in both the if and the else block out of the if-else statement.
  • Move if-else-if branches: Move an if-else branch up or down.
  • Move duplicated last statement out of if-else: Move a last statement that appears in both the if and the else block out of the if-else statement.
  • Move nested if: Push down if statements into nested if statements and pull nested if statements up.
  • Move switch case clause: Move a case clause in a switch statement up or down.
  • Remove {…} from if-else and loops: Replace single statement blocks with their inner statement.
  • Remove empty else block: Remove an empty 'else' block from an 'if' statement.
  • Remove empty if block: Remove an empty 'if' block from an 'if' statement. Replaces it with the 'else' block when available.
  • Remove redundant else if: Remove redundant else-if conditions and unreachable else statements.
  • Remove unnecessary conditional expression: Replace a conditional expression with its condition or its result.
  • Remove unnecessary else: Lift the else content of an if-else with a return statement to the outer indentation level.
  • Separate repeated condition into nested if-else: Separate a repeated sub-condition that is fully covered into a nested if-else.
  • Simplify switch statement: Remove an unnecessary switch statement or replace it with its content.
  • Split if statement: Split the condition of an if statement on || or && when possible.

Code Assists for Arrays and Loops

JavaScript has several ways of defining loops and many array methods that work on the whole array. The JS Assistant provides several code actions for converting between different types of for loops and for converting to more idiomatic array methods such as array.includes().

  • Convert array.filter()[0] to array.find(): Replace anArray.filter(…)[0] with anArray.find(…).
  • Convert array.indexOf() into array.includes(): Replace array.indexOf() checks with array.includes().
  • Convert string comparison chain to array.includes(): Replace || value === 'aString' and && value !== 'aString' chains with array.includes().
  • Convert loop to .forEach: Replace regular for loops with .forEach() loops.
  • Convert loop to for…of: Replace regular for loops and anArray.forEach loops with for…of loops.
  • Convert loop to for: Replace for…of with a regular for loop that has an index variable.
  • Convert loop to .map(): Convert a loop with .push() into a .map() call.
  • Move array element: Move an array element up or down.
  • Move object property: Move an object property up or down.

Code Assists for Functions and Methods

Functions and methods are essential building blocks of any non-trivial program. The following code actions make it easier to work with functions, methods, and their parameters:

  • Add {…} to arrow function: Convert arrow function expression body into a block body.
  • Convert function to arrow function: Replace function expressions with arrow functions, a more concise syntax.
  • Convert function to object method: Convert property assignments with functions to method declarations.
  • Convert named function declaration to variable declaration: Converts a named function to a const declaration with a function expression.
  • Move default value into parameter: Replace default value assignment expressions with default parameter values.
  • Push parameter into IIFE/IIAF: Push a parameter of an immediately-invoked function expressions (IIFEs) or an immediately-invoked arrow functions (IIAFs) into the function body.
  • Remove {…} from arrow function: Convert an arrow function block body into an expression body.
  • Remove IIFE/IIAF: Remove immediately-invoked function expressions (IIFEs) and immediately-invoked arrow functions (IIAFs) without parameters.

Code Assists for Classes

  • Convert #-private to TypeScript private: Replace ECMAScript #-private properties and methods with TypeScript private.
  • Convert TypeScript private to #-private: Replace TypeScript private properties and methods with ECMAScript #-private.
  • Move field initialization into constructor: Moves the assignment of the initial field value into the class constructor.
  • Move initialization into field declaration: Moves the assignment of the initial field value into the field declaration.

Code Assists for Strings and Template Literals

Text manipulation has become more powerful with the introduction of template literals in JavaScript. The JS Assistant offers several code actions to help you work with text, be it strings or template literals:

  • Convert string comparison chain to array.includes(): Replace || value === 'aString' and && value !== 'aString' chains with array.includes().
  • Convert string to template literal: Convert a string to a basic template literal without expressions.
  • Convert template literal to string: Convert a simple template literal without expressions into a string.
  • Extract selected text into variable: Extract the selected text (including expressions from template literals) into a const variable.
  • Inline into template: Inline a string or a basic template literal into an outer template literal.
  • Remove unnecessary template literal: Simplify a template literal with a single inner expression and no prefix or suffix.
  • Use string.endsWith(): string.endsWith() checks if a string ends with another string.
  • Use string.startsWith(): string.startsWith() checks if a string starts with another string.
  • Merge string concatenation: Merge string and template literal concatenation into a single template literal or string.

Code Assists for Variables

  • Convert destructuring to regular variable declaration: Convert all variables that are declared via destructuring into separate regular variable declarations.
  • Convert let to const: Replace let declarations that have no re-assignment with const declarations.
  • Convert 'new Array(…)' to '[…]': Replace new Array(…) calls with […].
  • Convert to destructuring assignment: Convert a variable declaration that accesses an object property to a destructuring assignment.
  • Extract variable: Extract one or more occurrences of an expression into a const variable.
  • Flatten array rest/spread property: Merge a ...[] expression into the outer array literal or destructuring expression.
  • Replace assignment with return: Convert a variable assignment to a return statement.
  • Move default value into parameter: Replace default value assignment expressions with default parameter values.
  • Merge into preceding destructuring assignment: Combine an object destructuring assignment with its preceding sibling.
  • Merge variable declaration and initialization: Convert the initial assignment of a variable into its declaration initializer.
  • Move constant to top-level scope: Move a constant to the top-level scope of the module.
  • Extract destructured expression into separate variable declaration: Move a destructured expression inside a variable declaration into a separate variable declaration.
  • Move field initialization into constructor: Moves the assignment of the initial field value into the class constructor.
  • Move initialization into field declaration: Moves the assignment of the initial field value into the field declaration.
  • Move variable declaration: Move a variable declaration up or down.
  • Push variable declaration into initial value: Inlines a variable that is initialized with another variable into the declaration of that variable.
  • Push parameter into IIFE/IIAF: Push a parameter of an immediately-invoked function expressions (IIFEs) or an immediately-invoked arrow functions (IIAFs) into the function body.
  • Remove trailing array destructuring holes: Remove trailing array destructuring holes and empty array destructuring expressions.
  • Remove unused variable: Remove a variable that is not read or written.
  • Replace with existing variable: Replace an expression with an existing variable.
  • Convert var to let or const: Replace var with block-scoped variables let and const.
  • Split variable declaration sequence: Convert declarations with multiple variables into separate declarations for each variable.
  • Split variable declaration and initialization: Separate the variable initialization from its declaration.
  • Use nullish coalescence in default expression: Replace default value expression with nullish coalescing operator (??) expressions.

Code Assists for Object and Array Destructuring

  • Convert destructuring to regular variable declaration: Convert all variables that are declared via destructuring into separate regular variable declarations.
  • Convert to destructuring assignment: Convert a variable declaration that accesses an object property to a destructuring assignment.
  • Merge into preceding destructuring assignment: Combine an object destructuring assignment with its preceding sibling.
  • Move destructuring array element: Move an element inside an array destructuring expression up or down.
  • Extract destructured expression into separate variable declaration: Move a destructured expression inside a variable declaration into a separate variable declaration.
  • Move destructuring object property: Move a property inside an object destructuring expression up or down.
  • Push variable declaration into initial value: Inlines a variable that is initialized with another variable into the declaration of that variable.
  • Remove trailing array destructuring holes: Remove trailing array destructuring holes and empty array destructuring expressions.

Code Assists for Syntax Conversion

It is often annoying to make small syntactical changes by editing text. Often more than one position needs to be edited, and the code is broken during the edit, leading to incorrect errors and auto-completions that get in the way. You can execute the following syntax conversions with code assists:

  • Add {…} to if-else and loops: Convert single statements into blocks.
  • Add {…} to arrow function: Convert arrow function expression body into a block body.
  • Add {…} to case: Surround case statements in a block.
  • Add {…} to JSX attribute: Add {…} to JSX attribute string literal value.
  • Add numeric separator: Increase the readability of long numbers and uncommon number formats by adding underscore separators.
  • Collapse JSX tag: Convert an empty JSX tag into a self-closing tag.
  • Collapse object property into shorthand: Shorten object properties when the property name is the same as the property value.
  • Convert property access to dot notation: Convert bracket notation property access o['a'] into dot notation property access o.a.
  • Convert property access to bracket notation: Convert dot notation property access o.a into bracket notation property access o['a'].
  • Convert to ++ / --: Convert an assignment expression into a ++ or -- expression.
  • Expand JSX tag: Expand a self-closing JSX tag.
  • Expand shorthand property: Expand a shorthand object property (e.g. { a }) to a regular property (e.g. { a: a }).
  • Merge variable declaration and initialization: Convert the initial assignment of a variable into its declaration initializer.
  • Pull operator out of assignment: Move an operator out of an assignment into a binary expression.
  • Push operator into assignment: Move an operator from a binary expression into an assignment operator, e.g., +=.
  • Remove {…} from if-else and loops: Replace single statement blocks with their inner statement.
  • Remove {…} from arrow function: Convert an arrow function block body into an expression body.
  • Remove {…} from case: Replace blocks with their content
  • Remove {…} from JSX attribute: Remove {…} from a JSX attribute expression value that contains a string literal.

JavaScript Modernizations

The Javascript ecosystem is progressing rapidly. However, it is hard to keep codebases up-to-date with the newer JavaScript features, and codemods are not always an option due to their significant churn and potential for breakages. The JS Assistant supports both codemod-like mass code refactoring and more opportunistic code modernization for the following upgrades:

  • Add numeric separator: Increase the readability of long numbers and uncommon number formats by adding underscore separators.
  • Collapse object property into shorthand: Shorten object properties when the property name is the same as the property value.
  • Convert .apply() to spread syntax: Replace .apply() calls with the spread operator ...
  • Convert array.indexOf() into array.includes(): Replace array.indexOf() checks with array.includes().
  • Convert string comparison chain to array.includes(): Replace || value === 'aString' and && value !== 'aString' chains with array.includes().
  • Convert function to arrow function: Replace function expressions with arrow functions, a more concise syntax.
  • Convert function to object method: Convert property assignments with functions to method declarations.
  • Convert loop to for…of: Replace regular for loops and anArray.forEach loops with for…of loops.
  • Convert Math.pow to exponentiation operator: Use the exponentiation operator ** instead of Math.pow().
  • Convert string to template literal: Convert a string to a basic template literal without expressions.
  • Convert to destructuring assignment: Convert a variable declaration that accesses an object property to a destructuring assignment.
  • Convert to optional chaining: Replace various guard expressions with the optional chaining operator (?.).
  • Move default value into parameter: Replace default value assignment expressions with default parameter values.
  • Convert var to let or const: Replace var with block-scoped variables let and const.
  • Replace void 0 with undefined: Replace void 0 and other constant void expressions with undefined.
  • Use == null comparison: Replace different nullish checks with == null.
  • Use nullish coalescence in default expression: Replace default value expression with nullish coalescing operator (??) expressions.
  • Use string.endsWith(): string.endsWith() checks if a string ends with another string.
  • Use string.startsWith(): string.startsWith() checks if a string starts with another string.
  • Merge string concatenation: Merge string and template literal concatenation into a single template literal or string.

Code Assists for TypeScript

  • Convert Type[] to Array: Change TypeScript array types into generic types.
  • Convert #-private to TypeScript private: Replace ECMAScript #-private properties and methods with TypeScript private.
  • Convert TypeScript private to #-private: Replace TypeScript private properties and methods with ECMAScript #-private.
  • Move interface member: Move a property, constructor or method definition up or down.
  • Move type member: Move a type member up or down.

Code Assists for React

In React, components often contain JSX, a syntax extension for JavaScript. The JS Assistant provides code assists that make working with JSX and React easier:

  • Add {…} to JSX attribute: Add {…} to JSX attribute string literal value.
  • Collapse JSX tag: Convert an empty JSX tag into a self-closing tag.
  • Expand JSX tag: Expand a self-closing JSX tag.
  • Extract React function component: Extract JSX element or fragment into a React Function Component.
  • Move JSX attribute: Move a JSX attribute up or down.
  • Remove {…} from JSX attribute: Remove {…} from a JSX attribute expression value that contains a string literal.
  • Remove unnecessary JSX fragment: Replace JSX Fragments <></> that only contain a single child with that child.
  • Surround with <>…</>: Wrap JSX elements in a JSX fragment <>…</>.

Lodash Modernizations

With the introduction of various collection helpers and new syntax in ES6 and more recent JavaScript versions, some Lodash functions have become somewhat redundant.

  • Replace _.every with array.every: Replace Lodash _.every with array.every.
  • Replace _.filter with array.filter: Replace Lodash _.filter with array.filter.
  • Replace _.each and _.forEach with array.forEach: Replace Lodash _.each and _.forEach with array.forEach.
  • Replace _.map with array.map: Replace Lodash _.map with array.map.
  • Replace _.noop with arrow Function: Replace _.noop with () => undefined.
  • Replace _.some with array.some: Replace Lodash _.some with array.some.

Code Assists for Moving Semantic Blocks

  • Move array element: Move an array element up or down.
  • Move class member: Move a property, constructor, or method definition up or down.
  • Move constant to top-level scope: Move a constant to the top-level scope of the module.
  • Move destructuring array element: Move an element inside an array destructuring expression up or down.
  • Extract destructured expression into separate variable declaration: Move a destructured expression inside a variable declaration into a separate variable declaration.
  • Move destructuring object property: Move a property inside an object destructuring expression up or down.
  • Move field initialization into constructor: Moves the assignment of the initial field value into the class constructor.
  • Move initialization into field declaration: Moves the assignment of the initial field value into the field declaration.
  • Move duplicated first statement out of if-else: Move a first statement that appears in both the if and the else block out of the if-else statement.
  • Move if-else-if branches: Move an if-else branch up or down.
  • Move interface member: Move a property, constructor or method definition up or down.
  • Move JSX attribute: Move a JSX attribute up or down.
  • Move duplicated last statement out of if-else: Move a last statement that appears in both the if and the else block out of the if-else statement.
  • Move nested if: Push down if statements into nested if statements and pull nested if statements up.
  • Move object property: Move an object property up or down.
  • Move statement: Move a statement up or down.
  • Move switch case clause: Move a case clause in a switch statement up or down.
  • Move type member: Move a type member up or down.
  • Move variable declaration: Move a variable declaration up or down.

Code Cleanups

Code cleanups remove unnecessary code. Such code can result from code churn, e.g., by applying other refactorings, adding new features, or fixing bugs. The JS Assistant shows hints and automates the cleanup for the following situations:

  • Flatten array rest/spread property: Merge a ...[] expression into the outer array literal or destructuring expression.
  • Remove console.log: Remove console.log statement.
  • Remove double negation: Remove double negation (!!) expressions.
  • Remove empty else block: Remove an empty 'else' block from an 'if' statement.
  • Remove empty if block: Remove an empty 'if' block from an 'if' statement. Replaces it with the 'else' block when available.
  • Remove IIFE/IIAF: Remove immediately-invoked function expressions (IIFEs) and immediately-invoked arrow functions (IIAFs) without parameters.
  • Remove redundant else if: Remove redundant else-if conditions and unreachable else statements.
  • Remove trailing array destructuring holes: Remove trailing array destructuring holes and empty array destructuring expressions.
  • Remove unnecessary conditional expression: Replace a conditional expression with its condition or its result.
  • Remove unnecessary else: Lift the else content of an if-else with a return statement to the outer indentation level.
  • Remove unnecessary expression statement: Remove an expression statement that has no side-effects.
  • Remove unnecessary JSX fragment: Replace JSX Fragments <></> that only contain a single child with that child.
  • Remove unnecessary template literal: Simplify a template literal with a single inner expression and no prefix or suffix.
  • Remove unused variable: Remove a variable that is not read or written.
  • Replace void 0 with undefined: Replace void 0 and other constant void expressions with undefined.
  • Simplify binary expression: Replace binary expression with a more straightforward equivalent expression.
  • Simplify switch statement: Remove an unnecessary switch statement or replace it with its content.

AI Actions

Actions that use the P42 Cloud AI (which needs to be enabled in the settings).

Other Actions

  • Insert console.log for variable: Insert a 'console.log' statement for a selected variable when possible.
  • Insert else statement: Add an else statement to an existing if statement
  • Select expression occurrences: Start a multi-cursor selection on several occurrences of the same expression.
  • Surround with if statement: Surround a sequence of statements with an if-statement.
  • Surround with try…catch: Surround a sequence of statements with a try…catch block.

Report Bugs and Suggest Features

Please report any bugs or feature suggestions in the JS Assistant issue tracker.

License & Used Open Source Libraries

See DISCLAIMER.txt.

js-assistant's People

Contributors

lgrammel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

js-assistant's Issues

Showing wrong hint for inline-return

Original code:

    public replaceVariables(path: Path, variables: { [key: string]: string }) {
        const me = this;
        let newPath;

        newPath = me.resolvePath(path);

        Object.keys(variables).forEach((variable: string) => {
            newPath.replace('{' + variable + '}', variables[variable]);
        });

        return newPath;
    }

Screenshot 2021-11-10 133109

code after the fix:

    public replaceVariables(path: Path, variables: { [key: string]: string }) {
        const me = this;
        let newPath;

        return me.resolvePath(path);

        //unreachable code after fix
        Object.keys(variables).forEach((variable: string) => {
            newPath.replace('{' + variable + '}', variables[variable]);
        });

    }

c

Idea: Support Renaming Unresolved Variables

After copy&pasting, I ended up with this code:

public extendInputRange(extendedInputRange: LineRange): LineRangeMapping {

    const startDelta = extendedInputRange.startLineNumber - l.inputRange.startLineNumber;
    const endDelta = extendedInputRange.endLineNumberExclusive - l.inputRange.endLineNumberExclusive;
    return new LineRangeMapping(
        inputRange,
        new LineRange(
            l.outputRange.startLineNumber + startDelta,
            l.outputRange.lineCount - startDelta + endDelta
        )
    );
}

Now I would like to replace l with this.
This is not very easy to do.

It would be awesome if I could rename l to this.

Refactor Idea: Move Up / Down Braces to Extend Scope

Move Brace:

if (!modifiedBaseRange || modifiedBaseRange.isConflicting) {
}

for (const diff of m.rights) {
    const range = diff.outputRange.toInclusiveRange();
    if (range) {
        result.push({
            range,
            options: {
                className: `merge-editor-diff result`,
                description: 'Merge Editor',
                isWholeLine: true,
            }
        });
    }

    if (diff.rangeMappings) {
        for (const d of diff.rangeMappings) {
            result.push({
                range: d.outputRange,
                options: {
                    className: `merge-editor-diff-word result`,
                    description: 'Merge Editor'
                }
            });
        }
    }
}

⟦}⟧ being selected.

--- Move down --->

if (!modifiedBaseRange || modifiedBaseRange.isConflicting) {

    for (const diff of m.rights) {
        const range = diff.outputRange.toInclusiveRange();
        if (range) {
            result.push({
                range,
                options: {
                    className: `merge-editor-diff result`,
                    description: 'Merge Editor',
                    isWholeLine: true,
                }
            });
        }

        if (diff.rangeMappings) {
            for (const d of diff.rangeMappings) {
                result.push({
                    range: d.outputRange,
                    options: {
                        className: `merge-editor-diff-word result`,
                        description: 'Merge Editor'
                    }
                });
            }
        }
    }
}

This should not be confused with move statement up/down:

if (!modifiedBaseRange || modifiedBaseRange.isConflicting) {
⟦⟧}

for (const diff of m.rights) {
    const range = diff.outputRange.toInclusiveRange();
    if (range) {
        result.push({
            range,
            options: {
                className: `merge-editor-diff result`,
                description: 'Merge Editor',
                isWholeLine: true,
            }
        });
    }

    if (diff.rangeMappings) {
        for (const d of diff.rangeMappings) {
            result.push({
                range: d.outputRange,
                options: {
                    className: `merge-editor-diff-word result`,
                    description: 'Merge Editor'
                }
            });
        }
    }
}

--- Move Down --->

for (const diff of m.rights) {
    const range = diff.outputRange.toInclusiveRange();
    if (range) {
        result.push({
            range,
            options: {
                className: `merge-editor-diff result`,
                description: 'Merge Editor',
                isWholeLine: true,
            }
        });
    }

    if (diff.rangeMappings) {
        for (const d of diff.rangeMappings) {
            result.push({
                range: d.outputRange,
                options: {
                    className: `merge-editor-diff-word result`,
                    description: 'Merge Editor'
                }
            });
        }
    }
}

if (!modifiedBaseRange || modifiedBaseRange.isConflicting) {
⟦⟧}

inline refactor bug in jsx

export function App() {
  const handleClick = () => console.log(test);
  return (
    <>
      <div onClick={handleClick}>test</div>
    </>
  );
}

If the handleClick function is refactored inline, the output is as follows. (<,t is repeated twice )

export function App() {
  return (
    <>
      <<div onClick={() => console.log(test)}>ttest</div>
    </>
  );
}

Code Action Idea: Reorder If/Else Branches

if (cond1) {
    body1
} el|se if (cond2) {
    body2
} else {
    body3
}

(| cursor)

-- Move Item Up ->

|if (cond2) {
    body2
} else if (cond1) {
    body1
} else {
    body3
}

Could use the same shortcut for reordering parameters/members etc.

Inline occurence issue

Previous Code
const temp3 = []; temp3[0] = temp1; temp3[1] = temp2;

Uploading image.png…

After refactoring
[][0] = temp1; [][1] = temp2;

Refactor idea: Refactor function to module scope, but preserve the function itself (with/out arguments)

Hi. Sometimes I use P42 for refactoring callbacks, so the code would be cleaner, but I always have to make a manual refactor to make it more performant. I want my callbacks to be preserved (instead of me

Initial:
image

Result:
image

Expected:
image

I'm not too familiar with this whole parsing and automatic refactoring thing and I don't know the thing I expect is possible or not. And I'm aware with arguments it will be more and more complicated But I believe the simple cases can be detected and put as an option.

UI is empty

image

the count changes when i open different .js files, but nothing shows in the UI.

Refactoring Idea: To/From Ternary

let originalRange: LineRange;
if (c.originalEndLineNumber === 0) {
    // Insertion
    originalRange = new LineRange(c.originalStartLineNumber + 1, c.originalStartLineNumber + 1);
} else {
    originalRange = new LineRange(c.originalStartLineNumber, c.originalEndLineNumber + 1);
}

=>

let originalRange: LineRange = c.originalEndLineNumber === 0
    // Insertion
    ? new LineRange(c.originalStartLineNumber + 1, c.originalStartLineNumber + 1)
    : new LineRange(c.originalStartLineNumber, c.originalEndLineNumber + 1);

Context:

export class SmartLinesDiffComputer implements ILinesDiffComputer {
	computeDiff(originalLines: string[], modifiedLines: string[], options: ILinesDiffComputerOptions): ILinesDiff {
		const diffComputer = new DiffComputer(originalLines, modifiedLines, {
			maxComputationTime: options.maxComputationTime,
			shouldIgnoreTrimWhitespace: options.ignoreTrimWhitespace,
			shouldComputeCharChanges: true,
			shouldMakePrettyDiff: true,
			shouldPostProcessCharChanges: true,
		});
		const result = diffComputer.computeDiff();
		return {
			quitEarly: result.quitEarly,
			changes: result.changes.map(c => {
				let originalRange: LineRange;
				if (c.originalEndLineNumber === 0) {
					// Insertion
					originalRange = new LineRange(c.originalStartLineNumber + 1, c.originalStartLineNumber + 1);
				} else {
					originalRange = new LineRange(c.originalStartLineNumber, c.originalEndLineNumber + 1);
				}

				let modifiedRange: LineRange;
				if (c.modifiedEndLineNumber === 0) {
					// Deletion
					modifiedRange = new LineRange(c.modifiedStartLineNumber + 1, c.modifiedStartLineNumber + 1);
				} else {
					modifiedRange = new LineRange(c.modifiedStartLineNumber, c.modifiedEndLineNumber + 1);
				}

				return {
					originalRange,
					modifiedRange,
					innerChanges: c.charChanges?.map(c => new RangeMapping(
						new Range(c.originalStartLineNumber, c.originalStartColumn, c.originalEndLineNumber, c.originalEndColumn),
						new Range(c.modifiedStartLineNumber, c.modifiedStartColumn, c.modifiedEndLineNumber, c.modifiedEndColumn),
					))
				};
			})
		};
	}
}

Convert to template string issue

Hey,
I noticed a error when refactoring this code:

fetch(
    process.env.API_URL +
      `/foo/bar?id=${id}&token=${token}`
  );

image

Result:

fetch(
    `${process.env.API_URL}/foo/bar?id=${id}&token=`token}`
  );

Best,
Tom

Provide a good story for string literal <-> template strings conversion

I usually start with a string literal ("hello world") and later want to convert it to `hello ${name}`.

While the code actions are nice, I think the story can be improved.

I'm thinking of automatic conversion from a normal string to a template string once the user types the first ${}, and the opposite direction once the users removes the last ${}.

Also, the conversion code action should keep the cursor at its position in the string (use two edits for the prefix and suffix instead a single one for the entire string).

Refactor Idea: Convert React Class Component to/from Function Component

function EditorDemo() {
	return <div className="p-5 mb-4">
		<h2>Editor</h2>

		<div className="mt-4 row row-cols-4">
			<div className="col">
				<label className="control-label">Language</label>
				<select className="language-picker"></select>
			</div>
			<div className="col">
				<label className="control-label">Theme</label>
				<select className="theme-picker">
					<option>Visual Studio</option>
					<option>Visual Studio Dark</option>
					<option>High Contrast Dark</option>
				</select>
			</div>
		</div>

		<div className="mt-2" style={{ height: 500, border: "1px solid gray" }}>
			<MonacoEditor
				model={undefined}
			/>
		</div>
	</div>;
}

<->

class EditorDemo {
	render() {
        return (
            <div className="p-5 mb-4">
                <h2>Editor</h2>

                <div className="mt-4 row row-cols-4">
                    <div className="col">
                        <label className="control-label">Language</label>
                        <select className="language-picker"></select>
                    </div>
                    <div className="col">
                        <label className="control-label">Theme</label>
                        <select className="theme-picker">
                            <option>Visual Studio</option>
                            <option>Visual Studio Dark</option>
                            <option>High Contrast Dark</option>
                        </select>
                    </div>
                </div>

                <div className="mt-2" style={{ height: 500, border: "1px solid gray" }}>
                    <MonacoEditor
                        model={undefined}
                    />
                </div>
            </div>;
        );
    }
}

Swapping if/else does not work

I thought this used to work:

recording

But it does not swap the branches.

If this is a who-wins thing, I would definitely expect the if branch to move when the cursor is on the if or else.

Bug with return statement suggestions

I found an example in the Arquero codebase where P42 makes buggy suggestions marked as "safe". The simplified snippet below involves a series of if-statement fallthroughs that can update the ast variable. P42 (correctly) notes that the assignment in the if(obj.window) branch can be replaced with a return statement. But afterward it continues suggesting similar replacements for other assignments which actually break the logic.

function test(obj) {
  if (obj.expr) {
    let ast;
    if (obj.field === true) {
      ast = obj.field;
    } else if (obj.func === true) {
      ast = obj.func;
    }
    if (ast) {
      if (obj.desc) {
        ast = { type: Descending, expr: ast };
      }
      if (obj.window) {
        ast = { type: Window, expr: ast, ...obj.window };
      }
      return ast;
    }
  }
}

Move In

this.debouncer.run(() => {
    
});
f|or (const handler of this._previewHandlers) {
    handler.handlePreview(state);
}

->

this.debouncer.run(() => {
    f|or (const handler of this._previewHandlers) {
        handler.handlePreview(state);
    }
});

push-operator-into-assignment error

I got this message “Can push + operator into += assignment.” for this line of code:

content = excerpt + content;

The proposed replacement is (which is marked as safe):

content += excerpt;

Which is not the same as I which to put excerpt before content in resulting content variable.

Smart extend keyboard short cut conflicts with default Move Line Up/Move Line Down

I am not sure if you noticed, but by default, Move Line Up is bound to Alt + UpArrow and Move Line Down is bound to Alt + DownArrow.

Given how incredibly useful Move Line Up/Down are, I would expect that everyone would be complaining that p42 just installed a conflicting set of keyboard bindings.

I am removing the bindings that p42 added. I am sure there is a better set of keys to bind the smart expand to.

Move Up/Down Code Actions

Personally, I think generic Up/Down Move Code Actions are extremely useful.
There should be two clever commands: Move Up and Move Down. Dependening on the selection and context, they do "the right". Because there are only two, they should have good keybindings.

Here is an (incomplete) collection of what they would enable:

  • Move Statements / Declarations
  • Move Declarations inside multi-variable declaration
  • Move Class Members
  • Move Array Items (taking care of commas)
    • Move array destructuring items
  • Move Object Properties (taking care of commas)
    • Move object destructuring properties
  • Move If/Else Branches #28
  • Move nested if-statements up/down
  • #41
  • Move expression in homogenous condition (a && |b && c -> |b && a && c) (note: overlap/replacement of 'flip operator')
  • Move argument in function invocation (f(1, |2) -> f(|2, 1))
  • Move Parameters (fixing all call-sites) (note: technically hard - requires multi-file, dataflow, etc.; more limited version possible)
  • Move JSX elements
  • Move JSX attributes
  • Move Case-Branches in Switch Statements
  • TypeScript:
    • move interface members
    • move type members
    • re-order union types

Sometimes a move is safe, sometimes not.

A key property of move commands should be that "Move up" and "Move down" are inverse.

Feature: global/user configuration

While I really enjoy this extension, the inability to disable something like Add Numeric Separator without polluting upstream repositories with my personal preferences, kinda annoy me.

Being able to configure stuff like that in VS Code User settings would help.

On a side note: As per p42 configuration

Adding a p42.toml to .gitignore and a p42.toml file to repository root, containing:

[refactoring.add-numeric-separator]
enable = false

Have zero effect ???
image

Code Action Idea: Fix Missing Argument From Context

class MyService {}

class Foo {
    constructor(
        private readonly myService: MyService
    ) {}

    blah() {
        bar('hello'); // Cursor is here
    }
}

function bar(baz: string, service: MyService) {
    // ...
}

image

Cursor is on the squiggles.


- Use missing argument from context ->

class MyService {}

class Foo {
    constructor(
        private readonly myService: MyService
    ) {}

    blah() {
        bar('hello', this.myService);
    }
}

function bar(baz: string, service: MyService) {
    // ...
}

- Use missing argument from parameter ->

class MyService {}

class Foo {
    constructor(
        private readonly myService: MyService
    ) {}

    blah(myService: MyServic) {
        bar('hello', myService);
    }
}

function bar(baz: string, service: MyService) {
    // ...
}

If you could record my code changes, you would see that parameter "wiring" is a task I do very often.

v1.29.0 causes the Extension Host to crash

Hi,

  • VSCode: 1.60.2
  • Extension: 1.29.0
  • OS: Windows 10.0.19043 x64

After starting VSCode, the error message "Extension host terminated unexpectedly" appears, with an option to restart it. After restarting it, it terminates again after a few seconds.

From the window log:
[2021-09-30 11:26:03.284] [renderer1] [error] Extension host terminated unexpectedly. The following extensions were running: aaron-bond.better-comments, mechatroner.rainbow-csv, vscode.extension-editing, vscode.typescript-language-features, christian-kohler.npm-intellisense, p42ai.refactor, VisualStudioExptTeam.vscodeintellicode, vscode.microsoft-authentication, ms-vscode.js-debug, vscode.debug-auto-launch, vscode.git, acarreiro.calculate, mhutchie.git-graph, PKief.material-icon-theme, rafamel.subtle-brackets, ritwickdey.LiveServer, vincaslt.highlight-matching-tag, vscode-icons-team.vscode-icons, wayou.vscode-todo-highlight, vscode.configuration-editing, vscode.json-language-features, vscode.npm

Checking the "Running Extensions" list, the P42 refactor extension is marked as unresponsive. Disabling the extension solves the problem (extension host does not terminate anymore).

Could you please look into this? TIA

Convert to map has issues with the ... operator

const combinedDiffs: { diff: RangeMapping; input: 1 | 2 }[] = [];

	for (const diffs of baseRange.input1Diffs) {
		combinedDiffs.push(...diffs.innerRangeMappings.map(diff => ({ diff, input: 1 as const })));
	}
	for (const diffs of baseRange.input2Diffs) {
		combinedDiffs.push(...diffs.innerRangeMappings.map(diff => ({ diff, input: 2 as const })));
	}

Convert to map ->

const combinedDiffs: { diff: RangeMapping; input: 1 | 2 }[] = baseRange.input1Diffs.map((diffs) => {
		return ...diffs.innerRangeMappings.map(diff => ({ diff, input: 1 as const }));
	});

	for (const diffs of baseRange.input2Diffs) {
		combinedDiffs.push(...diffs.innerRangeMappings.map(diff => ({ diff, input: 2 as const })));
	}

🐛

There is also flatMap!

Refactoring Idea: Use variable for expressions

It would be awesome to have a refactoring that can replace expressions with a variable.

In this screenshot, I would like to replace all occurences of flip(sourceNmbr) with targetNmbr:

image

const targetNmbr = flip(sourceNmbr);

if (firstBefore && firstBefore.getInputRange(sourceNmbr).contains(topLineNumber)) {
    sourceRange = firstBefore.getInputRange(sourceNmbr);
    targetRange = firstBefore.getInputRange(flip(sourceNmbr));
} else if (firstBefore && firstBefore.getInputRange(sourceNmbr).isEmpty && firstBefore.getInputRange(sourceNmbr).startLineNumber === topLineNumber) {
    sourceRange = firstBefore.getInputRange(sourceNmbr).deltaEnd(1);
    targetRange = firstBefore.getInputRange(flip(sourceNmbr)).deltaEnd(1);
} else {
    const delta = firstBefore ? firstBefore.getInputRange(flip(sourceNmbr)).endLineNumberExclusive - firstBefore.getInputRange(sourceNmbr).endLineNumberExclusive : 0;
    sourceRange = new LineRange(topLineNumber, 1);
    targetRange = new LineRange(topLineNumber + delta, 1);
}

This would basically be the second step after extracting a constant.

Bug report: Comment node is removed after "Extract to function in module scope"

Hi. When I refactor a callback in my JSX, which does something rather complicated, one of my comments are removed.

Code example:

//@ts-nocheck
/* eslint-disable */

export default function Something() {
  return (
    <SomeComponent
      aCallbackThatProducesAReactNode={() => {
        // this comment will be kept intact
        const someProps = {
          prop1: props.props1, // BUT THIS COMMENT WILL BE REMOVED :(

          prop2: props.props2 // this comment will be kept intact too
        };

        return <SomeOtherComponent {...someProps} />;
      }}
    />
  );
}

Try refactoring the whole callback into a function in module scope.

One:
image

Two:
image

Three:
image

As you see one of my comments are removed.

Thanks.

Convert if-else to conditional expression does not works with this.var

let x; if (me.isEnabledColumnFilter === true) { x = 'Disable Filters'; } else { x = 'Enable Filters'; }

Here, refactoring works perfectly and yield result is

let x; x = me.isEnabledColumnFilter === true ? 'Disable Filters' : 'Enable Filters';

But, the linting is not shown with this keyword. Below is the code.

if (this.isEnabledColumnFilter === true) { this.filterTitle = 'Disable Filters'; } else { this.filterTitle = 'Enable Filters'; }

Optional chaining conversion issue

P42 tooltip (v1.101.1)

You can shorten 'this.control && this.control.invalid && this.control.touched' expression with optional chaining.p42 use-optional-chaining

  return this.control && this.control.invalid && this.control.touched;

P42 suggests incorrectly to convert into

return this.control?.invalid?.touched;

Convert To Ternary

let diff = 0;
if (last) {
    diff = last.outputRange.endLineNumberExclusive - last.inputRange.endLineNumberExclusive;
}

=>

const diff = last ? last.outputRange.endLineNumberExclusive - last.inputRange.endLineNumberExclusive : 0;

Refactoring Idea: Early Return and Back

this._store.add(
    this.inputResultView.editor.onDidScrollChange(
        reentrancyBarrier.makeExclusive((c) => {
            if (c.scrollTopChanged) {
                synchronizeScrolling(this.inputResultView.editor, this.input1View.editor, input1ResultMapping.get(), 2);
                synchronizeScrolling(
                    this.inputResultView.editor,
                    this.input2View.editor,
                    input2ResultMapping.get(),
                    2
                );
            }
        })
    )
);

<=>

this._store.add(
    this.inputResultView.editor.onDidScrollChange(
        reentrancyBarrier.makeExclusive((c) => {
            if (!c.scrollTopChanged) {
                return;
            }
            
            synchronizeScrolling(this.inputResultView.editor, this.input1View.editor, input1ResultMapping.get(), 2);
            synchronizeScrolling(
                this.inputResultView.editor,
                this.input2View.editor,
                input2ResultMapping.get(),
                2
            );
        })
    )
);

Code Action Idea: Parallelize Awaits

await writeFile(ancestorUri, 'some content');
await writeFile(input1Uri, 'some content');
await writeFile(input2Uri, 'some content');
await writeFile(resultUri, 'some content');

<->

Promise.all([
    writeFile(ancestorUri, 'some content'),
    writeFile(input1Uri, 'some content'),
    writeFile(input2Uri, 'some content'),
    writeFile(resultUri, 'some content'),
])

Ideally with detection if there are any dependencies

Extension doesn't show any hints

Suggestions don't appear to be working in my vscode for some reason. I've put in some code that I know should trigger a refactor suggestion, but no suggestions appear on the code or the suggestions sidebar. I tried uninstalling and reinstalled and all of the suggestions settings are set to hint.

Does anyone have any suggestions on how I can fix this?

P42
v1.86.0

VScode
Version: 1.64.2 (user setup)
Commit: f80445acd5a3dadef24aa209168452a3d97cc326
Date: 2022-02-09T22:02:28.252Z
Electron: 13.5.2
Chromium: 91.0.4472.164
Node.js: 14.16.0
V8: 9.1.269.39-electron.0
OS: Windows_NT x64 10.0.22000

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.