From dfff05e5f5a6eb724e1b756472c3fa27f623f39f Mon Sep 17 00:00:00 2001 From: uctakeoff Date: Sat, 16 Dec 2023 18:45:37 +0900 Subject: [PATCH 1/2] feat: Change the line counting method #96 In line with the POSIX definition of lines in text files, lines that do not end with a newline (i.e., the last line) are not included in the count. However, the default setting is the same as before to avoid confusion. --- CHANGELOG.md | 63 ++++++++++----------------------------------- package.json | 7 ++++- package.nls.ja.json | 1 + package.nls.json | 1 + src/LineCounter.ts | 12 ++++++--- src/extension.ts | 34 ++++++++++++++++-------- 6 files changed, 53 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b51af3b..fb03dda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,143 +7,108 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how * workspace counter in status bar. -## [3.2.0] - +## [3.3.0] ### Changed +- Change the line counting method [#96](https://github.com/uctakeoff/vscode-counter/issues/96). + - In line with the POSIX definition of lines in text files, lines that do not end with a newline (i.e., the last line) are not included in the count.However, the default setting is the same as before to avoid confusion. -- Count the files in the top-level as a separate item #81 +## [3.2.2] +### Fixed +- Fixed Issue [#93](https://github.com/uctakeoff/vscode-counter/issues/93). -## [3.1.0] +## [3.2.1] +### Fixed +- Added spaces in statusbar +## [3.2.0] ### Changed +- Count the files in the top-level as a separate item #81 +## [3.1.0] +### Changed - Support storing collected language elsewhere #78 -## [3.0.0] +## [3.0.0] ### Changed - - Reviewed file detection rules ## [2.4.0] - ### Added - - Counter diff output function - ### Changed - - Moved the status bar to the right - ### Removed - - Disuse Configuration : `VSCodeCounter.outputMarkdownSeparately`. ## [2.3.0] - ### Fixed - - Error on large code bases ## [2.2.2] - ### Fixed - - Fixed Issue [#48](https://github.com/uctakeoff/vscode-counter/issues/48). ## [2.2.1] - ### Fixed - - Misconceptions about FileStat. ## [2.2.0] - ### Added - - New Configuration : `VSCodeCounter.history`. ## [2.1.0] - ### Added - - New Function : Count the range of the selected text. ## [2.0.0] - ### Added - - New Command : `Save the collected language configurations`. - ### Update - - This extension is no longer resident. - Don't save whether or not the program is shown in the status bar in the settings. ## [1.3.5] - ### Fixed - - Issue : `CSV could be more strictly formed`. ## [1.3.4] - ### Fixed - - Issue : `Handling symlinks`. ## [1.3.3] - ### Added - - New Command : `Check available languages`. ## [1.3.2] - ### Fixed - - Replaced the file API used with one provided by vscode. ## [1.3.1] - ### Fixed - - Problems that occur when `files.encoding` is set to a value other than utf8 ## [1.3.0] - ### Added - - Support Multi-root Workspaces. (Selection type) - ## [1.2.1] - ### Fixed - - Update some modules. ## [1.2.0] - ### Changed - - Output Markdown summary and details separately. (selectable by settings.json) - ## [1.1.1] - ### Added - - resolve file types using ["files.associations"](https://code.visualstudio.com/docs/languages/overview#_adding-a-file-extension-to-a-language) setting. - ## [1.0.1] ### Fixed - - Error on large code bases ## [1.0.0] ### Fixed - - Auto ignore the .VSCodeCounter directory. ## [0.1.0] diff --git a/package.json b/package.json index a461e1f..e6a0ac9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-counter", "displayName": "VS Code Counter", "description": "Count lines of code in many programming languages.", - "version": "3.2.2", + "version": "3.3.0", "publisher": "uctakeoff", "author": { "name": "Ushiyama Kentaro" @@ -110,6 +110,11 @@ "type": "boolean", "default": true }, + "VSCodeCounter.includeIncompleteLine": { + "description": "%configuration.includeIncompleteLine.description%", + "type": "boolean", + "default": true + }, "VSCodeCounter.endOfLine": { "description": "%configuration.endOfLine.description%", "type": "string", diff --git a/package.nls.ja.json b/package.nls.ja.json index 159d8d0..b9b6685 100644 --- a/package.nls.ja.json +++ b/package.nls.ja.json @@ -10,6 +10,7 @@ "configuration.maxOpenFiles.description": "VSCodeCounter が同時に読み取ることのできるファイルの最大数.", "configuration.printNumberWithCommas.description": "数値をカンマ区切りで出力する (CSVを除く).", "configuration.ignoreUnsupportedFile.description": "サポートしないファイルを無視する.", + "configuration.includeIncompleteLine.description": "改行で終わらない行(最終行)を含める.", "configuration.endOfLine.description": "出力ファイルで使用する改行文字.", "configuration.exclude.description": "ファイルとフォルダーを除外するための glob パターン.", "configuration.include.description": "ファイルとフォルダーを追加するための glob パターン.", diff --git a/package.nls.json b/package.nls.json index 1092ee2..8740672 100644 --- a/package.nls.json +++ b/package.nls.json @@ -10,6 +10,7 @@ "configuration.maxOpenFiles.description": "Maximum number of files that VSCodeCounter can read simultaneously.", "configuration.printNumberWithCommas.description": "Whether to print a number with commas as thousands separators.(except for CSV)", "configuration.ignoreUnsupportedFile.description": "Ignore unsupported files.", + "configuration.includeIncompleteLine.description": "Include lines that do not end in a newline (last line).", "configuration.endOfLine.description": "A new line character to be used in the output file.", "configuration.exclude.description": "Configure glob patterns for excluding files and folders.", "configuration.include.description": "Configure glob patterns for including files and folders.", diff --git a/src/LineCounter.ts b/src/LineCounter.ts index b99ba02..fae4803 100644 --- a/src/LineCounter.ts +++ b/src/LineCounter.ts @@ -50,14 +50,18 @@ export class LineCounter { private lineComments: string[], private blockComments: [string, string][], private blockStrings: [string, string][]) { - this.blockCommentBegins = this.blockComments.map(b => b[0]); - this.blockStringBegins = this.blockStrings.map(b => b[0]); + this.blockCommentBegins = this.blockComments.map(b => b[0]); + this.blockStringBegins = this.blockStrings.map(b => b[0]); } - public count(text: string): Count { + public count(text: string, includeIncompleteLine = false): Count { let result = [0, 0, 0]; let blockCommentEnd = ''; let blockStringEnd = ''; - text.split(/\r\n|\r|\n/).map(line => line.trim()).forEach((line, lineIndex) => { + const lines = text.split(/\r\n|\r|\n/).map(line => line.trim()); + if (!includeIncompleteLine) { + lines.pop(); + } + lines.forEach((line, lineIndex) => { let type = (blockCommentEnd.length > 0) ? LineType.Comment : (blockStringEnd.length > 0) ? LineType.Code : LineType.Blank; let i = 0; while (i < line.length) { diff --git a/src/extension.ts b/src/extension.ts index 046e005..33a0b59 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -85,6 +85,7 @@ const loadConfig = () => { history: Math.max(1, conf.get('history', 5)), languages: conf.get<{ [key: string]: Partial }>('languages', {}), + includeIncompleteLine: conf.get('includeIncompleteLine', false), endOfLine: conf.get('endOfLine', '\n'), printNumberWithCommas: conf.get('printNumberWithCommas', true), outputPreviewType: conf.get('outputPreviewType', ''), @@ -230,7 +231,13 @@ class CodeCounterController { } const counter = await this.getCodeCounter(); - const results = await countLines(counter, targetFiles, this.conf.maxOpenFiles, this.conf.encoding, this.conf.ignoreUnsupportedFile, (msg: string) => statusBar.text = `VSCodeCounter: ${msg}`); + const results = await countLines(counter, targetFiles, { + maxOpenFiles: this.conf.maxOpenFiles, + fileEncoding: this.conf.encoding, + ignoreUnsupportedFile: this.conf.ignoreUnsupportedFile, + includeIncompleteLine: this.conf.includeIncompleteLine, + showStatus: (msg: string) => statusBar.text = `VSCodeCounter: ${msg}` + }); if (results.length <= 0) { throw Error(`There was no target file.`); } @@ -267,7 +274,7 @@ class CodeCounterController { const lineCounter = c.getCounter(doc.uri.fsPath, doc.languageId); if (lineCounter) { const result = editor.selections - .map(s => lineCounter.count(doc.getText(s))) + .map(s => lineCounter.count(doc.getText(s), this.conf.includeIncompleteLine)) .reduce((prev, cur) => prev.add(cur), new Count()); this.showStatusBar(`Selected Code: ${result.code} Comment: ${result.comment} Blank: ${result.blank}`); } else { @@ -282,7 +289,7 @@ class CodeCounterController { const c = await this.getCodeCounter(); const lineCounter = c.getCounter(doc.uri.fsPath, doc.languageId); if (lineCounter) { - const result = lineCounter?.count(doc.getText()); + const result = lineCounter?.count(doc.getText(), this.conf.includeIncompleteLine); this.showStatusBar(`Code: ${result.code} Comment: ${result.comment} Blank: ${result.blank}`); } else { this.showStatusBar(); @@ -311,15 +318,21 @@ const loadGitIgnore = async () => { const values = await readUtf8Files(gitignoreFiles.sort()); return new Gitignore('').merge(...values.map(p => new Gitignore(p.data, dirUri(p.uri).fsPath))); } - -const countLines = (lineCounterTable: LineCounterTable, fileUris: vscode.Uri[], maxOpenFiles: number, fileEncoding: string, ignoreUnsupportedFile: boolean, showStatus: (text: string) => void) => { +type CountLineOption = { + maxOpenFiles: number; + fileEncoding: string; + ignoreUnsupportedFile?: boolean; + includeIncompleteLine?: boolean; + showStatus?: (text: string) => void; +}; +const countLines = (lineCounterTable: LineCounterTable, fileUris: vscode.Uri[], option: CountLineOption) => { log(`countLines : target ${fileUris.length} files`); return new Promise(async (resolve: (value: Result[]) => void, reject: (reason: string) => void) => { const results: Result[] = []; if (fileUris.length <= 0) { resolve(results); } - const decoder = createTextDecoder(fileEncoding); + const decoder = createTextDecoder(option.fileEncoding); const totalFiles = fileUris.length; let fileCount = 0; const onFinish = () => { @@ -333,16 +346,15 @@ const countLines = (lineCounterTable: LineCounterTable, fileUris: vscode.Uri[], const fileUri = fileUris[i]; const lineCounter = lineCounterTable.getCounter(fileUri.fsPath); if (lineCounter) { - - while ((i - fileCount) >= maxOpenFiles) { + while ((i - fileCount) >= option.maxOpenFiles) { // log(`sleep : total:${totalFiles} current:${i} finished:${fileCount} valid:${results.length}`); - showStatus(`${fileCount}/${totalFiles}`); + option.showStatus?.(`${fileCount}/${totalFiles}`); await sleep(50); } vscode.workspace.fs.readFile(fileUri).then(data => { try { - results.push(new Result(fileUri, lineCounter.name, lineCounter.count(decoder.decode(data)))); + results.push(new Result(fileUri, lineCounter.name, lineCounter.count(decoder.decode(data), option.includeIncompleteLine))); } catch (e: any) { log(`"${fileUri}" Read Error : ${e.message}.`); results.push(new Result(fileUri, '(Read Error)')); @@ -355,7 +367,7 @@ const countLines = (lineCounterTable: LineCounterTable, fileUris: vscode.Uri[], onFinish(); }); } else { - if (!ignoreUnsupportedFile) { + if (!option.ignoreUnsupportedFile) { results.push(new Result(fileUri, '(Unsupported)')); } onFinish(); From 8817d4fb23763c8cab50e114e0009185ba7b3580 Mon Sep 17 00:00:00 2001 From: uctakeoff Date: Sat, 16 Dec 2023 19:28:51 +0900 Subject: [PATCH 2/2] feat: Enable the real-time counter at start-up. However, it should not be resident. --- CHANGELOG.md | 2 ++ package.json | 1 + src/extension.ts | 31 +++++++++++++++++++++---------- src/vscode-utils.ts | 6 ++++-- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb03dda..31be710 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ### Changed - Change the line counting method [#96](https://github.com/uctakeoff/vscode-counter/issues/96). - In line with the POSIX definition of lines in text files, lines that do not end with a newline (i.e., the last line) are not included in the count.However, the default setting is the same as before to avoid confusion. +- Enable the real-time counter at start-up. + - However, it should not be resident. ## [3.2.2] ### Fixed diff --git a/package.json b/package.json index e6a0ac9..e44cef7 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "line" ], "activationEvents": [ + "workspaceContains:**/.VSCodeCounterCountRealtime", "onCommand:extension.vscode-counter.countInFile", "onCommand:extension.vscode-counter.countInDirectory", "onCommand:extension.vscode-counter.countInWorkspace", diff --git a/src/extension.ts b/src/extension.ts index 33a0b59..8aa1eb1 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -12,6 +12,7 @@ import { internalDefinitions } from './internalDefinitions'; const EXTENSION_ID = 'uctakeoff.vscode-counter'; const EXTENSION_NAME = 'VSCodeCounter'; const CONFIGURATION_SECTION = 'VSCodeCounter'; +const REALTIME_COUNTER_FILE = '.VSCodeCounterCountRealtime'; const toZeroPadString = (num: number, fig: number) => num.toString().padStart(fig, '0'); const toLocalDateString = (date: Date, delims: [string, string, string] = ['-', ' ', ':']) => { return `${date.getFullYear()}${delims[0]}${toZeroPadString(date.getMonth() + 1, 2)}${delims[0]}${toZeroPadString(date.getDate(), 2)}${delims[1]}` @@ -116,6 +117,12 @@ class CodeCounterController { // create a combined disposable from both event subscriptions this.disposable = vscode.Disposable.from(...subscriptions); + + currentWorkspaceFolder().then((workFolder) => { + vscode.workspace.fs.stat(buildUri(workFolder.uri, this.conf.outputDirectory, REALTIME_COUNTER_FILE)).then(st => { + this.toggleVisible(); + }); + }); } dispose() { this.statusBarItem?.dispose(); @@ -159,9 +166,15 @@ class CodeCounterController { if (!this.statusBarItem) { this.statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100000); this.countLinesInEditor(vscode.window.activeTextEditor); + currentWorkspaceFolder().then((workFolder) => { + writeTextFile(buildUri(workFolder.uri, this.conf.outputDirectory, REALTIME_COUNTER_FILE), '', {recursive: true}); + }); } else { this.statusBarItem.dispose(); this.statusBarItem = null; + currentWorkspaceFolder().then((workFolder) => { + vscode.workspace.fs.delete(buildUri(workFolder.uri, this.conf.outputDirectory, REALTIME_COUNTER_FILE)); + }); } } @@ -455,14 +468,12 @@ const saveLanguageConfigurations = async (langs: { [key: string]: LanguageConf } break; case "output directory":{ const workFolder = await currentWorkspaceFolder(); - const outputDir = buildUri(workFolder.uri, conf.outputDirectory); - await makeDirectories(outputDir); - await writeTextFile(outputDir, 'languages.json', JSON.stringify(langs)); + await writeTextFile(buildUri(workFolder.uri, conf.outputDirectory, 'languages.json'), JSON.stringify(langs), {recursive: true}); break; } case "use languageConfUri":{ const workFolder = await currentWorkspaceFolder(); - await writeTextFile(parseUriOrFile(conf.languageConfUri, workFolder.uri), undefined, JSON.stringify(langs)); + await writeTextFile(parseUriOrFile(conf.languageConfUri, workFolder.uri), JSON.stringify(langs), {recursive: true}); break; } default: break; @@ -501,7 +512,7 @@ const previewFiles = new Map([ ]); const outputResults = async (date: Date, targetDirUri: vscode.Uri, results: Result[], outputDir: vscode.Uri, prevOutputDir: vscode.Uri | undefined, conf: Config) => { await makeDirectories(outputDir); - writeTextFile(outputDir, `results.json`, resultsToJson(results)); + writeTextFile(buildUri(outputDir, `results.json`), resultsToJson(results)); const resultTable = new ResultFormatter(targetDirUri, results, conf); log(`OutputDir : ${outputDir}, count ${results.length} files`); @@ -534,12 +545,12 @@ const outputResults = async (date: Date, targetDirUri: vscode.Uri, results: Resu const diffTable = new ResultFormatter(targetDirUri, diffs, conf); if (conf.outputAsText) { - await writeTextFile(outputDir, 'results.txt', resultTable.toTextLines(date)); - await writeTextFile(outputDir, 'diff.txt', diffTable.toTextLines(date)); + await writeTextFile(buildUri(outputDir, 'results.txt'), resultTable.toTextLines(date)); + await writeTextFile(buildUri(outputDir, 'diff.txt'), diffTable.toTextLines(date)); } if (conf.outputAsCSV) { - await writeTextFile(outputDir, 'results.csv', resultTable.toCSVLines()); - await writeTextFile(outputDir, 'diff.csv', diffTable.toCSVLines()); + await writeTextFile(buildUri(outputDir, 'results.csv'), resultTable.toCSVLines()); + await writeTextFile(buildUri(outputDir, 'diff.csv'), diffTable.toCSVLines()); } if (conf.outputAsMarkdown) { const mds = [ @@ -549,7 +560,7 @@ const outputResults = async (date: Date, targetDirUri: vscode.Uri, results: Resu { title: 'Diff Details', path: 'diff-details.md', table: diffTable, detail: true }, ]; await Promise.all(mds.map(({ title, path, table, detail }, index) => { - return writeTextFile(outputDir, path, table.toMarkdown(date, title, detail, mds.map((f, i) => [f.title, i === index ? undefined : f.path]))); + return writeTextFile(buildUri(outputDir, path), table.toMarkdown(date, title, detail, mds.map((f, i) => [f.title, i === index ? undefined : f.path]))); })); } const previewFile = previewFiles.get(conf.outputPreviewType); diff --git a/src/vscode-utils.ts b/src/vscode-utils.ts index b289585..ed4a923 100644 --- a/src/vscode-utils.ts +++ b/src/vscode-utils.ts @@ -159,8 +159,10 @@ export const showTextPreview = async (uri: vscode.Uri) => { await showTextFile(uri); } } -export const writeTextFile = async (baseUri: vscode.Uri, path: string | undefined, text: string) => { - const uri = path ? buildUri(baseUri, path) : baseUri; +export const writeTextFile = async (uri: vscode.Uri, text: string, option?: {recursive?: boolean}) => { + if (option?.recursive) { + await makeDirectories(dirUri(uri)); + } // log(`writeTextFile : ${uri} ${text.length}B`); await vscode.workspace.fs.writeFile(uri, encoderU8.encode(text)); return uri;