p42ai / js-assistant Goto Github PK
View Code? Open in Web Editor NEW120+ refactorings and code-assists for Visual Studio Code
Home Page: https://marketplace.visualstudio.com/items?itemName=p42ai.refactor
License: MIT License
120+ refactorings and code-assists for Visual Studio Code
Home Page: https://marketplace.visualstudio.com/items?itemName=p42ai.refactor
License: MIT License
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).
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>
</>
);
}
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
Hi,
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
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.
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.
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:
a && |b && c
-> |b && a && c
) (note: overlap/replacement of 'flip operator')f(1, |2)
-> f(|2, 1)
)Sometimes a move is safe, sometimes not.
A key property of move commands should be that "Move up" and "Move down" are inverse.
return name ? `From Event: ${name}` : 'From Event';
->
return 'From Event' + (name ? `: ${name}` + '');
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
.
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
);
})
)
);
Hi @lgrammel,
Linting for convert-to-for-of-loop / convert-to-foreach-loop is not showing. Also, no refactoring option is available for to convert simple for loop to for-of loop.
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.
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),
))
};
})
};
}
}
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>;
);
}
}
Hi @lgrammel ,
Please refer the below snaps for the issue.
check for the index variable i
in forEach loop after refactoring.
Hello
Is it possible to exclude some directories from mass refactorings? I only found a way to exclude files by adding the annotation but ignore entire folders does not seem to be possible?
Thanks
let diff = 0;
if (last) {
diff = last.outputRange.endLineNumberExclusive - last.inputRange.endLineNumberExclusive;
}
=>
const diff = last ? last.outputRange.endLineNumberExclusive - last.inputRange.endLineNumberExclusive : 0;
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!
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 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) {
⟦⟧}
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;
const baz = window.foo?.bar.baz;
should not change to
const { baz } = window.foo?.bar;
const arr = new Array<T>();
<->
const arr: T[] = [];
The second one is faster (by 5-10%).
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
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.
As you see one of my comments are removed.
Thanks.
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);
}
});
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
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.
Can we have an option to convert if else if to switch case?
I am using tslint but it only shows the linting but does not refactors it.
class MyService {}
class Foo {
constructor(
private readonly myService: MyService
) {}
blah() {
bar('hello'); // Cursor is here
}
}
function bar(baz: string, service: MyService) {
// ...
}
Cursor is on the squiggles.
class MyService {}
class Foo {
constructor(
private readonly myService: MyService
) {}
blah() {
bar('hello', this.myService);
}
}
function bar(baz: string, service: MyService) {
// ...
}
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.
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;
}
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]);
});
}
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
:
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.
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
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'; }
test
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.