vscode extension API editor.replace replace only first match while match 2 instances - vscode-extensions

developing an VS code extension where
search for color hex code in whole css document
replace the color hex code with variable name
although it match all color hex code but replace the only first instance and stops
below is the code snippet
export function activate(context: vscode.ExtensionContext) {
let activeEditor = vscode.window.activeTextEditor;
function replaceWithinDocument() {
if (!activeEditor) {
return;
}
const text = activeEditor.document.getText();
const reg = new RegExp('(?<color>#[0-9a-f]{3,6})', 'gim');
const matches = text.matchAll(reg);
const variableList = {};
let i = 0;
for (const match of matches) {
const { index, groups } = match;
i++;
console.log({ match });
const startPos = activeEditor.document.positionAt(index!);
const endPos = activeEditor.document.positionAt(index! + match[0].length);
console.log({ i, startPos, endPos });
//Creating a new range with startLine, startCharacter & endLine, endCharacter.
let range = new vscode.Range(startPos, endPos);
// eslint-disable-next-line #typescript-eslint/naming-convention
Object.assign(variableList, { [`--var-${i}`]: groups?.color });
activeEditor.edit(editBuilder => {
editBuilder.replace(range, `--var-${i}`);
});
}
console.log({ variableList });
}
function triggerUpdateDecorations(throttle = false) {
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
if (throttle) {
timeout = setTimeout(replaceWithinDocument, 500);
} else {
replaceWithinDocument();
}
}
if (activeEditor) {
triggerUpdateDecorations();
}
final document
body {
background-color: --var-1;
color: #223344;
}
you can see in the screenshot that console.log({ variableList }); have both color code in it
so what is wrong here?

See allow delay between edits via vscode extension api. Because of the particular nature of the editBuilder object
The editBuilder "expires" once you return from the callback passed to
TextEditor.edit.
you should put your matches loop inside the call to the edit call like this sample code:
// get your matches above first
editor.edit(editBuilder => {
let i = 0;
for (const match of matches) {
// build your replacement here
const matchStartPos = document.positionAt(match.index);
const matchEndPos = document.positionAt(match.index + match[0].length);
const matchRange = new Range(matchStartPos, matchEndPos);
editBuilder.replace(matchRange, resolvedReplace);
}
}).then(async (resolved) => {
});

Related

Cloudflare ESI worker / TypeError: Body has already been used

I'm trying to use a CloudFlare worker to manage my backend ESI fragments but i get an error:
Uncaught (in promise) TypeError: Body has already been used. It can only be used once. Use tee() first if you need to read it twice.
Uncaught (in response) TypeError: Body has already been used. It can only be used once. Use tee() first if you need to read it twice.
I don't find where the body has already been used
The process is:
get a response with the parts
Transform the body by replacing parts fragments with sub Backend calls (streamTransformBody function)
return the response
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
});
const esiHeaders = {
"user-agent": "cloudflare"
}
async function handleRequest(request) {
// get cookies from the request
if(cookie = request.headers.get("Cookie")) {
esiHeaders["Cookie"] = cookie
console.log(cookie)
}
// Clone the request so that it's no longer immutable
newRequest = new Request(request)
// remove cookie from request
newRequest.headers.delete('Cookie')
// Add header to get <esi>
newRequest.headers.set("Surrogate-Capability", "abc=ESI/1.0")
console.log(newRequest.url);
const response = await fetch(newRequest);
let contentType = response.headers.get('content-type')
if (!contentType || !contentType.startsWith("text/")) {
return response
}
// Clone the response so that it's no longer immutable
const newResponse = new Response(response.body, response);
let { readable, writable } = new TransformStream()
streamTransformBody(newResponse.body, writable)
newResponse.headers.append('x-workers-hello', 'Hello from
Cloudflare Workers');
return newResponse;
}
async function streamTransformBody(readable, writable) {
const startTag = "<".charCodeAt(0);
const endTag = ">".charCodeAt(0);
let reader = readable.getReader();
let writer = writable.getWriter();
let templateChunks = null;
while (true) {
let { done, value } = await reader.read();
if (done) break;
while (value.byteLength > 0) {
if (templateChunks) {
let end = value.indexOf(endTag);
if (end === -1) {
templateChunks.push(value);
break;
} else {
templateChunks.push(value.subarray(0, end));
await writer.write(await translate(templateChunks));
templateChunks = null;
value = value.subarray(end + 1);
}
}
let start = value.indexOf(startTag);
if (start === -1) {
await writer.write(value);
break;
} else {
await writer.write(value.subarray(0, start));
value = value.subarray(start + 1);
templateChunks = [];
}
}
}
await writer.close();
}
async function translate(chunks) {
const decoder = new TextDecoder();
let templateKey = chunks.reduce(
(accumulator, chunk) =>
accumulator + decoder.decode(chunk, { stream: true }),
""
);
templateKey += decoder.decode();
return handleTemplate(new TextEncoder(), templateKey);
}
async function handleTemplate(encoder, templateKey) {
const linkRegex = /(esi:include.*src="(.*?)".*\/)/gm
let result = linkRegex.exec(templateKey);
let esi
if (!result) {
return encoder.encode(`<${templateKey}>`);
}
if (result[2]) {
esi = await subRequests(result[2]);
}
return encoder.encode(
`${esi}`
);
}
async function subRequests(target){
target = esiHost + target
const init = {
method: 'GET',
headers: esiHeaders
}
let response = await fetch(target, init)
if (!response.ok) {
return ''
}
let text = await response.text()
return '<!--esi-->' + text + '<!--/esi-->'
}

ngx-dropzone to load images from list

I am using ngx-dropzone with angular 8. I have used ngx-dropzone for uploading and it works but this time I want to load images from a specific list to dropzone. Here is my code.
<ngx-dropzone (change)="onSelect($event)">
<ngx-dropzone-label>Select/Drop images here!</ngx-dropzone-label>
<ngx-dropzone-image-preview ngProjectAs="ngx-dropzone-preview" [removable]="true" (removed)="onRemove(f)" *ngFor="let f of files" [file]="f">
<ngx-dropzone-label></ngx-dropzone-label>
</ngx-dropzone-image-preview>
</ngx-dropzone>
Here is my onSelect event which bindimages and push them into files array list.
onSelect(event) {
this.alertService.clear();
console.log(event.addedFiles);
this.files.push(...event.addedFiles);
if (this.files.length > 4) {
this.alertService.error('Please select only four images for each service.');
this.files = [];
} else {
this.bindImages();
}
}
bindImages() {
this.alertService.clear();
this.imageList = [];
this.files.forEach((x) => {
const file = x;
if (file.type.split('/')[0] !== 'image') {
this.alertService.error('Please select image to proceed further.');
return false;
}
if (file.size > 5242880) {
this.alertService.error('Image size must be equal to or less then 5 MB.');
return false;
}
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
const val = reader.result as string;
const items = [{Serviceimage: val, ServiceId: this.serviceId, SaloonId: this.saloonId}];
this.imageList.push(...items);
};
reader.onerror = (error) => {
console.log('Error: ', error);
return;
};
});
}
Here is the my api codes which returns me the list of images with base64 string
getImages() {
this.alertService.clear();
this.imageModel.SaloonId = this.saloonId;
this.imageModel.ServiceId = this.serviceId;
this.apiService.Create('Saloon/getServiceImages', this.imageModel).subscribe(
resp => {
if (resp.length > 0) {
// Here to load Images to dropzone.
}
console.log(this.files);
},
error => {
this.alertService.error('Error getting images. Please contact admin.');
},
() => { console.log('complete'); }
);
}
This image returns me the following list.

$nextTick running before previous line finished

I have a vue function call which is triggered when selecting a radio button but it seems that my code inside my $nextTick is running before my previous line of code is finished. I don't want to use setTimout as I don't know how fast the user connection speed is.
findOrderer() {
axios.post('/MY/ENDPOINT')
.then((response) => {
this.orderers = response.data.accounts;
console.log('FIND_ORDER', this.orderers)
...OTHER_CODE
}
rbSelected(value) {
this.findOrderer();
this.newOrderList = [];
this.$nextTick(() => {
for (var i = 0, length = this.orderers.length; i < length; i++) {
console.log('FOR')
if (value.srcElement.value === this.orderers[i].accountType) {
console.log('IF')
this.newOrderList.push(this.orderers[i]);
}
}
this.$nextTick(() => {
this.orderers = [];
this.orderers = this.newOrderList;
console.log('orderers',this.orderers)
})
})
}
Looking at the console log the 'FINE_ORDERER' console.log is inside the 'findOrderer' function call so I would have expected this to be on top or am I miss using the $nextTick
That's expected, since findOrderer() contains asynchronous code. An easy way is to simply return the promise from the method, and then await it instead of waiting for next tick:
findOrderer() {
return axios.post('/MY/ENDPOINT')
.then((response) => {
this.orderers = response.data.accounts;
console.log('FIND_ORDER', this.orderers);
});
},
rbSelected: async function(value) {
// Wait for async operation to complete first!
await this.findOrderer();
this.newOrderList = [];
for (var i = 0, length = this.orderers.length; i < length; i++) {
console.log('FOR')
if (value.srcElement.value === this.orderers[i].accountType) {
console.log('IF')
this.newOrderList.push(this.orderers[i]);
}
}
this.orderers = [];
this.orderers = this.newOrderList;
console.log('orderers',this.orderers)
}

How to write unit test component with FileReader.addEventListener in angular 8?

I use angular 8 and i want to test my component with FileReader.
I can not test a FileReader in my processFile function.
Maybe my work is badly written? Can you help me please to understand.
IF I understand correctly, I have to test a class (Filereader) in a process function
my component
processFile(imageInput: any) {
const file: File = imageInput.files[0];
const reader = new FileReader();
let size: number = 2097152
if (file) {
if (file.size <= size) {
this.sharingDataService.setUploadIsOk(true)
reader.addEventListener('progress', (event:any) =>{
this.progressValue = this.progressBar(event)
if (event.lengthComputable) {
// console.log(event.loaded+ " / " + event.total)
}
})
reader.addEventListener('loadstart', (event:any) =>{
this.progressValue =0;
this.textDuringUploadBefore = "No"
this.textDuringUploadAfter = ''
// console.log('start');
})
reader.addEventListener('loadend', (event:any) =>{
// console.log('end');
})
reader.addEventListener('load', (event: any) => {
console.log(event);
this.selectedFile = new ImageSnippet(event.target.result, file);
this.fileName = this.selectedFile.file.name;
this.fileNameExt =this.fileName.split('.').pop();
this.displayAddPhoto = false;
this.selectedFile.status = 'ok';
this.getLoadCallBack(file)
// this.ng2ImgMax.resizeImage(file, 900,600).subscribe(
// result =>{
// // console.log('compress', );
// this.textDuringUploadAfter= "Yes!!!"
// this.textDuringUploadBefore= ''
// this.fileForm.patchValue({
// image: new File([result], result.name)
// });
// this.imageIsLoad = true
// this.sharingDataService.setUploadIsOk(false)
// }
// )
// this.imageOutput.emit(this.fileForm)
});
reader.readAsDataURL(file);
} else {
const msg ="This picture is too big."
+ '<br/>' + "Please upload an image of less than 2MB."
// this.sharedFunctionService.openDialogAlert(msg, 'home')
this.errorService.openDialogError(msg)
this.imageIsLoad = false
this.sharingDataService.setUploadIsOk(false)
}
}
}
getLoadCallBack(file:File){
this.ng2ImgMax.resizeImage(file, 900,600).subscribe(
result =>{
// console.log('compress', );
this.textDuringUploadAfter= "Yes"
this.textDuringUploadBefore= ''
this.fileForm.patchValue({
image: new File([result], result.name)
});
console.log(this.fileForm);
this.imageIsLoad = true
this.sharingDataService.setUploadIsOk(false)
}
)
this.imageOutput.emit(this.fileForm)
}
my spec.ts
it('processFile', () => {
// const mockEvt = { target: { files: [fileInput] } };
// const mockReader: FileReader = jasmine.createSpyObj('FileReader', ['readAsDataURL', 'onload']);
// spyOn(window as any, 'FileReader').and.returnValue(mockReader);
// spyOn(component, 'getLoadCallBack').and.callThrough();
const file = new File([''], 'test-file.jpg', { lastModified: null, type: 'image/jpeg' });
const fileInput = { files: [file] };
const eventListener = jasmine.createSpy();
spyOn(window as any, "FileReader").and.returnValue({
addEventListener: eventListener
})
component.processFile(fileInput);
i have got an error
TypeError: reader.readAsDataURL is not a function
how to test my processFile function?
I trie many way but no sucess

Wrong queryString when building navigation plan in aurelia-router

We're using aurelia-open-id-connect in our project and after successful login the redirect is removing the querystring from the url. I've debugged my way through the aurelia-router and I think I found something. Should'nt the queryString in redirectInstruction be used there?
export const buildRedirectPlan = (instruction: NavigationInstruction) => {
const config = instruction.config;
const router = instruction.router;
return router
._createNavigationInstruction(config.redirect)
.then(redirectInstruction => {
const params: Record<string, any> = {};
const originalInstructionParams = instruction.params;
const redirectInstructionParams = redirectInstruction.params;
for (let key in redirectInstructionParams) {
// If the param on the redirect points to another param, e.g. { route: first/:this, redirect: second/:this }
let val = redirectInstructionParams[key];
if (typeof val === 'string' && val[0] === ':') {
val = val.slice(1);
// And if that param is found on the original instruction then use it
if (val in originalInstructionParams) {
params[key] = originalInstructionParams[val];
}
} else {
params[key] = redirectInstructionParams[key];
}
}
let redirectLocation = router.generate(redirectInstruction.config, params, instruction.options);
// Special handling for child routes
for (let key in originalInstructionParams) {
redirectLocation = redirectLocation.replace(`:${key}`, originalInstructionParams[key]);
}
let queryString = instruction.queryString;
// use redirectInstruction.queryString instead?
if (queryString) {
redirectLocation += '?' + queryString;
}
return Promise.resolve(new Redirect(redirectLocation));
});
};