139 private randomLogo: RandomLogo =
new RandomLogo();
143 private documentBody:
vscode.TextDocument | undefined = undefined;
145 private filePath:
string | undefined = undefined;
147 private fileName:
string | undefined = undefined;
149 private fileExtension:
string | undefined = undefined;
151 private languageId:
string | undefined = undefined;
153 private documentEOL:
vscode.EndOfLine | undefined = undefined;
155 private documentVersion: number | undefined = undefined;
157 private headerInnerStart: number | undefined = undefined;
159 private headerInnerEnd: number | undefined = undefined;
161 private maxScanLength: number = this.Config.get(
"maxScanLength");
163 private headerLogo:
string[] = this.Config.get(
"headerLogo");
165 private projectName:
string = this.Config.get(
"extensionName");
167 private projectCopyRight:
string = this.Config.get(
"projectCopyright");
169 private addBlankLineAfterMultiline:
boolean = this.Config.get(
"headerAddBlankLineAfterMultiline");
180 constructor(languageComment:
LazyFileLoader | undefined = undefined, editor:
vscode.TextEditor | undefined = undefined, randomLogoInstance: RandomLogo | undefined = undefined) {
182 if (languageComment !== undefined) {
183 this.languageComment = languageComment;
191 this.updateFileInfo(editor);
194 if (!randomLogoInstance) {
210 private determineNewLine(eol:
vscode.EndOfLine):
string {
211 logger.debug(
getMessage(
"inFunction",
"determineNewLine",
"CommentGenerator"));
212 if (eol ===
vscode.EndOfLine.LF) {
213 logger.debug(getMessage(
"foundNewLine",
"\\n"));
216 logger.debug(getMessage(
"foundNewLine",
"\\r\\n"));
231 private headerOpener(comment:
string, eol:
vscode.EndOfLine, projectName:
string =
this.Config.get(
"extensionName")):
string {
233 let
final:
string = comment + this.Config.get(
"headerOpenerDecorationOpen");
234 final += this.Config.get(
"telegraphBegin") +
" ";
235 final += projectName;
236 final += this.Config.get(
"headerOpenerDecorationClose");
237 final += this.determineNewLine(eol);
249 private async determineCorrectComment(): Promise<
CommentStyle> {
250 logger.debug(
getMessage(
"inFunction",
"determineCorrectComment",
"CommentGenerator"));
251 const primaryKey:
string =
"langs";
255 prompt_comment_opening_type:
false,
257 if (this.languageComment === undefined) {
259 return commentStructure;
261 const jsonContent = await this.languageComment?.get();
263 if (!jsonContent || typeof jsonContent !==
'object' || jsonContent ===
null || (primaryKey in jsonContent) ===
false) {
265 return commentStructure;
267 if (Array.isArray(jsonContent[primaryKey]) ===
false) {
269 return commentStructure;
272 const languageNodes = jsonContent[primaryKey];
273 let locatedName:
string =
"";
274 for (; index < languageNodes.length; index++) {
275 let nodeFound =
false;
276 const node = languageNodes[index];
278 const nodeLangs:
string[] = node.langs ?? [];
279 const nodeFileExtensions: Record<string, string[]> = node.fileExtensions ?? {};
281 for (let langIndex = 0; langIndex < nodeLangs.length; langIndex++) {
282 const langName = nodeLangs[langIndex].toLowerCase();
283 locatedName = langName;
285 if (langName === this.languageId?.toLowerCase()) {
290 if (this.fileExtension) {
291 const extensionsForLang = nodeFileExtensions[langName] ?? [];
292 for (let extIndex = 0; extIndex < extensionsForLang.length; extIndex++) {
293 let dot:
string =
"";
294 if (extensionsForLang[extIndex].length > 0 && extensionsForLang[extIndex][0] ===
".") {
297 if (extensionsForLang[extIndex] === `${dot}${this.fileExtension}`) {
309 logger.debug(
getMessage(
"arrayNodeContent", `Json[${primaryKey}]`, index, node));
312 logger.info(
getMessage(
"arrayNodeContent", `Json[${primaryKey}]`, index, node));
313 commentStructure.singleLine = node.singleLine ?? [];
314 commentStructure.multiLine = node.multiLine ?? [];
315 commentStructure.prompt_comment_opening_type = node.prompt_comment_opening_type ??
false;
316 return commentStructure;
319 logger.error(
getMessage(
"languageNotFound", String(this.languageId), this.fileExtension));
320 return commentStructure;
330 private async determineHeaderDescription(): Promise<
string[]> {
331 logger.debug(
getMessage(
"inFunction",
"determineHeaderDescription",
"CommentGenerator"));
332 let
final:
string[] = [];
333 const usrProjectDescription:
string = this.Config.get(
"projectDescription");
334 if (usrProjectDescription.length === 0) {
335 const usrResponse:
string | undefined = await
query.input(
getMessage(
"getHeaderDescription"));
336 final.push(usrResponse ||
"");
339 final.push(usrProjectDescription);
351 private async determineHeaderTags(): Promise<
string[]> {
352 logger.debug(
getMessage(
"inFunction",
"determineHeaderTags",
"CommentGenerator"));
353 let
final:
string[] = [];
354 const usrResponse:
string | undefined = await
query.input(
getMessage(
"getHeaderTags"));
355 final.push(usrResponse ||
"");
366 private async determineHeaderPurpose(): Promise<
string[]> {
367 logger.debug(
getMessage(
"inFunction",
"determineHeaderPurpose",
"CommentGenerator"));
368 let
final:
string[] = [];
369 const usrResponse:
string | undefined = await
query.input(
getMessage(
"getHeaderPurpose"));
370 final.push(usrResponse ||
"");
384 private async getSingleCommentOption(commentOptions:
string[]): Promise<
string> {
385 logger.debug(
getMessage(
"inFunction",
"getSingleCommentOption",
"CommentGenerator"));
386 if (commentOptions.length === 0) {
390 if (commentOptions.length === 1) {
392 return commentOptions[0];
394 const response:
string | undefined = await
query.quickPick(commentOptions,
getMessage(
"chooseSingleLineCommentOption"));
395 return response || commentOptions[0];
405 private addKeyDefinitionSeparator():
string {
406 logger.debug(
getMessage(
"inFunction",
"addKeyDefinitionSeparator",
"CommentGenerator"));
407 const userSettingDefinedElement:
string = this.Config.get(
"headerKeyDefinitionSeparator");
408 return userSettingDefinedElement || this.Config.get(
"headerKeyDefinitionSeparator");
420 private addCreationDate(comment:
string, eol:
vscode.EndOfLine) {
421 logger.debug(
getMessage(
"inFunction",
"addCreationDate",
"CommentGenerator"));
422 const now =
new Date();
423 const day = String(now.getDate()).padStart(2,
"0");
424 const month = String(now.getMonth() + 1).padStart(2,
"0");
425 const year = now.getFullYear();
426 const separatorDay:
string = this.Config.get(
"headerDateSeperatorDay");
427 const separatorMonth:
string = this.Config.get(
"headerDateSeperatorMonth");
428 const separatorYear:
string = this.Config.get(
"headerDateSeperatorYear");
429 let
final:
string = comment + this.Config.get(
"headerCreationDateKey") + this.addKeyDefinitionSeparator();
430 final += `${day}${separatorDay}${month}${separatorMonth}${year}${separatorYear}`;
431 final += this.determineNewLine(eol);
445 private addLastModifiedDate(comment:
string, eol:
vscode.EndOfLine) {
446 logger.debug(
getMessage(
"inFunction",
"addLastModifiedDate",
"CommentGenerator"));
447 const now: Date =
new Date();
448 const day:
string = String(now.getDate()).padStart(2,
"0");
449 const month:
string = String(now.getMonth() + 1).padStart(2,
"0");
450 const year: number = now.getFullYear();
451 const hour: number = now.getHours();
452 const minute: number = now.getMinutes();
453 const seconds: number = now.getSeconds();
454 const separatorDay:
string = this.Config.get(
"headerDateSeperatorDay");
455 const separatorMonth:
string = this.Config.get(
"headerDateSeperatorMonth");
456 const separatorYear:
string = this.Config.get(
"headerDateSeperatorYear");
457 const separatorTimeAndDate:
string = this.Config.get(
"headerTimeAndDateSeperator");
458 const separatorSecond:
string = this.Config.get(
"headerTimeSeperatorSecond");
459 const separatorMinute:
string = this.Config.get(
"headerTimeSeperatorMinute");
460 const separatorHour:
string = this.Config.get(
"headerTimeSeperatorHour");
461 let
final:
string = comment + this.Config.get(
"headerLastModifiedKey") + this.addKeyDefinitionSeparator();
462 final += `${hour}${separatorHour}${minute}${separatorMinute}${seconds}${separatorSecond}${separatorTimeAndDate}${day}${separatorDay}${month}${separatorMonth}${year}${separatorYear}`;
463 final += this.determineNewLine(eol);
479 private addMultilineKey(comment:
string, eol:
vscode.EndOfLine, tagName:
string, tagDefinition:
string[]):
string {
480 logger.debug(
getMessage(
"inFunction",
"addMultilineKey",
"CommentGenerator"));
481 const eolStr:
string = this.determineNewLine(eol);
482 let
final:
string = comment + tagName + this.addKeyDefinitionSeparator() + eolStr;
483 for (let i = 0; i < tagDefinition.length; i++) {
484 final += comment + tagDefinition[i] + eolStr;
486 final += comment + this.Config.get(
"telegraphBlockStop") + eolStr;
487 if (this.addBlankLineAfterMultiline) {
488 final += comment + eolStr;
503 private addSingleLineKey(comment:
string, eol:
vscode.EndOfLine, tagName:
string, tagDefinition:
string):
string {
504 logger.debug(
getMessage(
"inFunction",
"addSingleLineKey",
"CommentGenerator"));
505 let
final:
string = comment + tagName + this.addKeyDefinitionSeparator();
506 final += tagDefinition + this.determineNewLine(eol);
519 private beforeHeaderCloser(comment:
string, eol:
vscode.EndOfLine):
string {
520 logger.debug(
getMessage(
"inFunction",
"beforeHeaderCloser",
"CommentGenerator"));
521 return comment + this.Config.get(
"telegraphEndOfTransmission") + this.determineNewLine(eol);
534 private headerCloser(comment:
string, eol:
vscode.EndOfLine, projectName:
string =
this.Config.get(
"extensionName")):
string {
536 let
final:
string = comment + this.Config.get(
"headerOpenerDecorationOpen");
537 final += this.Config.get(
"telegraphEnd") +
" ";
538 final += projectName;
539 final += this.Config.get(
"headerOpenerDecorationClose");
540 final += this.determineNewLine(eol);
553 private updateFileInfo(editor:
vscode.TextEditor, document:
vscode.TextDocument | undefined = undefined) {
554 logger.debug(
getMessage(
"inFunction",
"updateFileInfo",
"CommentGenerator"));
555 this.headerInnerEnd = undefined;
556 this.headerInnerStart = undefined;
557 if (document === undefined) {
558 this.documentBody = editor.document;
560 this.documentBody = document;
562 if (this.Config.get(
"useWorkspaceNameWhenAvailable")) {
563 this.projectName = this.Config.get(
"workspaceName") || this.Config.get(
"extensionName");
565 this.projectName = this.Config.get(
"extensionName");
567 this.filePath = this.documentBody.uri.fsPath;
568 this.projectCopyRight = this.Config.get(
"projectCopyright");
569 this.addBlankLineAfterMultiline = this.Config.get(
"headerAddBlankLineAfterMultiline");
570 this.maxScanLength = this.Config.get(
"defaultMaxScanLength") + this.headerLogo.length;
571 this.fileName = this.documentBody.uri.path.split(
'/').pop() ||
"unknown";
572 if (this.fileName.includes(
'.')) {
573 this.fileExtension = this.fileName.split(
'.').pop() ||
"none";
575 this.fileExtension =
"none";
577 this.languageId = this.documentBody.languageId;
578 this.documentEOL = this.documentBody.eol;
579 this.documentVersion = this.documentBody.version;
591 private async getCorrectCommentPrefix(determinedComment:
CommentStyle): Promise<
string[]> {
592 logger.debug(
getMessage(
"inFunction",
"getCorrectPrefix",
"CommentGenerator"));
593 let commentOpener:
string =
"";
594 let commentMiddle:
string =
"";
595 let commentCloser:
string =
"";
596 if (determinedComment.multiLine.length >= 2) {
597 commentOpener = determinedComment.multiLine[0];
598 if (determinedComment.multiLine.length >= 3) {
599 commentMiddle = determinedComment.multiLine[1];
600 commentCloser = determinedComment.multiLine[2];
603 commentCloser = determinedComment.multiLine[1];
605 }
else if (determinedComment.singleLine.length > 0) {
606 if (determinedComment.prompt_comment_opening_type) {
608 const commentString:
string = await this.getSingleCommentOption(determinedComment.singleLine);
609 commentOpener = commentString;
610 commentMiddle = commentString;
611 commentCloser = commentString;
613 commentOpener = determinedComment.singleLine[0];
614 commentMiddle = determinedComment.singleLine[0];
615 commentCloser = determinedComment.singleLine[0];
622 commentOpener += this.Config.get(
"headerCommentSpacing");
623 commentMiddle += this.Config.get(
"headerCommentSpacing");
624 commentCloser += this.Config.get(
"headerCommentSpacing");
625 return [commentOpener, commentMiddle, commentCloser];
643 private async buildTheHeader(comments:
string[]): Promise<
string[]> {
644 logger.debug(
getMessage(
"inFunction",
"buildTheHeader",
"CommentGenerator"));
645 const eol:
vscode.EndOfLine = this.documentEOL ||
vscode.EndOfLine.LF;
646 const unknownTerm:
string =
getMessage(
"unknown");
647 const commentOpener:
string = comments[0] ||
"";
648 const commentMiddle:
string = comments[1] ||
"";
649 const commentCloser:
string = comments[2] ||
"";
650 let buildHeader:
string[] = [];
652 if (commentOpener.length > 0) {
653 buildHeader.push(`${commentOpener}${this.determineNewLine(eol)}`);
656 buildHeader.push(this.headerOpener(commentMiddle, eol, this.projectName));
658 let logoContent = this.headerLogo;
659 if (this.Config.get(
"randomLogo") ===
true) {
661 const gatheredLogo:
logo = await this.
randomLogo.getRandomLogoFromFolder();
662 if (gatheredLogo.logoContent !== undefined) {
663 logoContent = gatheredLogo.logoContent;
671 buildHeader.push(this.addMultilineKey(commentMiddle, eol, this.Config.get(
"headerLogoKey"), logoContent));
673 buildHeader.push(this.addSingleLineKey(commentMiddle, eol, this.Config.get(
"headerProjectKey"),
this.projectName));
675 buildHeader.push(this.addSingleLineKey(commentMiddle, eol, this.Config.get(
"headerFileKey"),
this.fileName || unknownTerm));
677 buildHeader.push(this.addCreationDate(commentMiddle, eol));
679 buildHeader.push(this.addLastModifiedDate(commentMiddle, eol));
681 buildHeader.push(this.addMultilineKey(commentMiddle, eol, this.Config.get(
"headerDescriptionKey"), await
this.determineHeaderDescription()));
683 buildHeader.push(this.addSingleLineKey(commentMiddle, eol, this.Config.get(
"headerCopyrightKey"),
this.projectCopyRight));
687 buildHeader.push(this.addSingleLineKey(commentMiddle, eol, this.Config.get(
"headerPurposeKey"), (await
this.determineHeaderPurpose()).join(
";")));
689 buildHeader.push(this.beforeHeaderCloser(commentMiddle, eol));
691 buildHeader.push(this.headerCloser(commentMiddle, eol, this.projectName));
692 if (commentCloser.length > 0) {
693 buildHeader.push(`${commentCloser}${this.determineNewLine(eol)}`);
707 private async updateEditDate(editor:
vscode.TextEditor, comments:
string[]) {
708 logger.debug(
getMessage(
"inFunction",
"updateEditDate",
"CommentGenerator"));
709 const commentOpener:
string = comments[0] ||
"";
710 const commentMiddle:
string = comments[1] ||
"";
711 const commentCloser:
string = comments[2] ||
"";
712 if (this.headerInnerStart === undefined || this.headerInnerEnd === undefined) {
713 const errMsg:
string =
getMessage(
"updateEditDateMissingBounds");
718 if (!this.documentBody) {
719 const errMsg:
string =
getMessage(
"emptyDocument");
725 const eol = this.documentEOL ||
vscode.EndOfLine.LF;
726 const lastModifiedKey = this.Config.get(
"headerLastModifiedKey");
729 const newLine = this.addLastModifiedDate(commentMiddle, eol).trimEnd();
731 let targetLine: number | undefined = undefined;
734 for (let i = this.headerInnerStart; i <= this.headerInnerEnd; i++) {
735 const lineText = this.documentBody.lineAt(i).text;
736 if (lineText.includes(lastModifiedKey)) {
742 if (targetLine === undefined) {
743 const errMsg:
string =
getMessage(
"lastModifiedLineNotFound");
749 await editor.edit(editBuilder => {
750 const range = this.documentBody!.lineAt(targetLine!).range;
751 editBuilder.replace(range, newLine);
754 const msg:
string =
getMessage(
"lastModifiedUpdated");
769 logger.debug(
getMessage(
"inFunction",
"locateIfHeaderPresent",
"CommentGenerator"));
770 const commentOpener:
string = comments[0] ||
"";
771 const commentMiddle:
string = comments[1] ||
"";
772 const commentCloser:
string = comments[2] ||
"";
773 this.headerInnerStart = undefined;
774 this.headerInnerEnd = undefined;
775 if (this.documentBody === undefined) {
779 if (this.documentBody.isClosed) {
780 logger.warning(getMessage(
"closedDocument"));
783 const eol:
vscode.EndOfLine =
this.documentEOL ??
vscode.EndOfLine.LF;
784 const opener:
string =
this.headerOpener(commentMiddle, eol,
this.projectName);
785 const closer:
string =
this.headerCloser(commentMiddle, eol,
this.projectName);
786 const scanLines: number = Math.min(
this.maxScanLength,
this.documentBody.lineCount);
787 let lineOpenerFound:
boolean =
false;
788 let lineCloserFound:
boolean =
false;
789 for (let i = 0; i < scanLines; i++) {
790 const lineText = this.documentBody.lineAt(i).text;
791 if (lineText === opener.trimEnd() && lineCloserFound) {
795 if (lineText === closer.trimEnd() && !lineOpenerFound) {
799 if (lineText === opener.trimEnd() && (!lineCloserFound && !lineOpenerFound)) {
800 lineOpenerFound =
true;
801 this.headerInnerStart = i;
805 if (lineText === closer.trimEnd() && (!lineCloserFound && lineOpenerFound)) {
806 this.headerInnerEnd = i;
811 logger.Gui.warning(
getMessage(
"documentLineScanExceeded", this.maxScanLength));
824 private async writeHeaderToFile(editor:
vscode.TextEditor, comments:
string[]): Promise<number> {
825 logger.debug(
getMessage(
"inFunction",
"writeHeaderToFile",
"CommentGenerator"));
826 let offset: number = 0;
827 const headerContent:
string[] = await this.buildTheHeader(comments);
829 const headerString:
string = headerContent.join(
"");
830 await editor.edit(editBuilder => editBuilder.insert(
new vscode.Position(offset, 0), headerString));
831 return this.Config.get(
"statusSuccess");
844 async injectHeader() {
846 const editor =
vscode.window.activeTextEditor;
847 if (editor === undefined) {
852 this.updateFileInfo(editor);
855 this.documentBody === undefined || this.filePath === undefined
856 || this.fileName === undefined || this.fileExtension === undefined
857 || this.languageId === undefined || this.documentEOL === undefined
858 || this.documentVersion === undefined
865 if (this.documentBody.isClosed) {
867 logger.Gui.warning(
getMessage(
"updateAbortedBecauseFileClosedSyncCancelled"));
871 const msg =
getMessage(
"inputArgs", JSON.stringify(
this.documentBody), JSON.stringify(
this.filePath), JSON.stringify(
this.fileName), JSON.stringify(
this.fileExtension), JSON.stringify(
this.languageId), JSON.stringify(
this.documentEOL), JSON.stringify(
this.documentVersion));
875 const determineComment:
CommentStyle = await this.determineCorrectComment();
876 const comments:
string[] = await this.getCorrectCommentPrefix(determineComment);
878 let response:
boolean | undefined = this.locateIfHeaderPresent(comments);
879 if (response === undefined) {
880 logger.Gui.warning(
getMessage(
"updateAbortedBecauseFileClosedSyncCancelled"));
883 if (response ===
true) {
885 await this.updateEditDate(editor, comments);
888 if (response ===
false) {
889 let status: number = await this.writeHeaderToFile(editor, comments);
890 if (status === this.Config.get(
"statusError")) {
906 private async allowedToActivate(): Promise<boolean> {
907 logger.debug(
getMessage(
"inFunction",
"allowedToActivate",
"CommentGenerator"));
908 const ignored:
string[] =
CodeConfig.get(
"extensionIgnore") ?? [];
910 for (
const pattern of ignored) {
912 this.fileExtension && minimatch(this.fileExtension, pattern) ||
913 this.fileName && minimatch(this.fileName, pattern) ||
914 this.filePath && minimatch(this.filePath, pattern)
931 updateLogoInstanceRandomiser(randomLogoInstance: RandomLogo): void {
932 logger.debug(
getMessage(
"inFunction",
"updateLogoInstanceRandomiser",
"CommentGenerator"));
946 async refreshHeader(document:
vscode.TextDocument | undefined) {
947 logger.debug(
getMessage(
"inFunction",
"refreshHeader",
"CommentGenerator"));
953 const editor =
vscode.window.activeTextEditor;
954 if (editor === undefined || document === undefined) {
959 this.updateFileInfo(editor, document);
962 this.documentBody === undefined || this.filePath === undefined
963 || this.fileName === undefined || this.fileExtension === undefined
964 || this.languageId === undefined || this.documentEOL === undefined
965 || this.documentVersion === undefined
972 if (!await this.allowedToActivate()) {
977 if (this.documentBody.isClosed) {
979 logger.Gui.warning(
getMessage(
"updateAbortedBecauseFileClosedSyncCancelled"));
983 const msg =
getMessage(
"inputArgs", JSON.stringify(
this.documentBody), JSON.stringify(
this.filePath), JSON.stringify(
this.fileName), JSON.stringify(
this.fileExtension), JSON.stringify(
this.languageId), JSON.stringify(
this.documentEOL), JSON.stringify(
this.documentVersion));
987 const determineComment:
CommentStyle = await this.determineCorrectComment();
988 const comments:
string[] = await this.getCorrectCommentPrefix(determineComment);
990 const response:
boolean | undefined = this.locateIfHeaderPresent(comments);
991 if (response === undefined) {
992 logger.Gui.warning(
getMessage(
"updateAbortedBecauseFileClosedSyncCancelled"));
995 if (response ===
false) {
1001 const questionResponse:
boolean = await
query.confirm(
getMessage(
"headerInjectQuestion"));
1002 if (questionResponse ===
false) {
1006 const status: number = await this.writeHeaderToFile(editor, comments);
1007 if (status === this.Config.get(
"statusError")) {
1013 if (response ===
true) {
1015 await this.updateEditDate(editor, comments);