From 218a71dccc7c684c89ae5bd332b553b56ed46525 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 17 Mar 2024 12:54:36 -0400 Subject: [PATCH 1/5] Add base functionality: git commit and git check repos --- package-lock.json | 46 +++++++++++++++++++++++++++++++++++----- package.json | 5 +++-- src/extension.ts | 13 ++++++++++++ src/log.ts | 1 + src/transformProvider.ts | 30 ++++++++++++++++++++++++++ tsconfig.json | 3 ++- 6 files changed, 90 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index a2d4f0b..22c7574 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,21 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "requires": { + "debug": "^4.1.1" + } + }, + "@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true + }, "@types/node": { "version": "12.19.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.7.tgz", @@ -175,6 +190,15 @@ } } }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -452,6 +476,12 @@ "integrity": "sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=", "dev": true }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -700,6 +730,17 @@ "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", "dev": true }, + "simple-git": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.23.0.tgz", + "integrity": "sha512-P9ggTW8vb/21CAL/AmnACAhqBDfnqSSZVpV7WuFtsFR9HLunf5IqQvk+OXAQTfkcZep8pKnt3DV3o7w3TegEkQ==", + "dev": true, + "requires": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.4" + } + }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -793,11 +834,6 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==" - }, "typescript": { "version": "3.9.7", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", diff --git a/package.json b/package.json index fe516c9..9490ff9 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "type": "boolean", "default": false }, - "tsar-advisor.advanced.analysisServer" : { + "tsar-advisor.advanced.analysisServer": { "description": "Analysis server which is used to analyze a project. Set path to the analysis server executable if it is not available in the PATH environment variable.", "type": "string", "default": "tsar-server" @@ -374,7 +374,8 @@ "@types/node": "^12.8.1", "catw": "~1.0.1", "npm-run-all": "~4.1.5", - "rimraf": "~3.0.0" + "rimraf": "~3.0.0", + "simple-git": "~3.23.0" }, "extensionDependencies": [], "dependencies": { diff --git a/src/extension.ts b/src/extension.ts index 00ce98e..e7e0076 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -23,6 +23,7 @@ import * as t from './transformProvider'; import server from './tools'; import { FileListProvider } from './fileList'; import { LoopTreeViewProvider } from './loopExplorer'; +import simpleGit, { SimpleGit } from 'simple-git' /** * Open log file (log.Extension.log), returns true on success. @@ -89,6 +90,18 @@ export function activate(context: vscode.ExtensionContext) { project.providerState(FileListProvider.scheme).active = true; project.send(new msg.FileList); vscode.commands.executeCommand('tsar.function.list', project.uri); + + let git = simpleGit(path.dirname(project.uri.fsPath)); + git.checkIsRepo() + .then(async (isRepo) => { + if (!isRepo) { + await git.init(); + } + }) + let GitFilePath = path.join(path.dirname(project.uri.fsPath), '.tsar_git'); + if (!fs.existsSync(GitFilePath)) { + fs.writeFileSync(GitFilePath, '', 'utf-8'); // here can be written information that should be saved for git + } }, reason => { onReject(reason, uri) }) }); diff --git a/src/log.ts b/src/log.ts index 5e0cae1..ff8426a 100644 --- a/src/log.ts +++ b/src/log.ts @@ -46,6 +46,7 @@ export class Project { } export class Error { + static gitIgnore = '{0} is in .gitignore'; static serverNotFound = 'cannot find analysis server {0}'; static serverVersion = 'unable to determine server version' static general = 'some errors have been occurred'; diff --git a/src/transformProvider.ts b/src/transformProvider.ts index add431e..349f625 100644 --- a/src/transformProvider.ts +++ b/src/transformProvider.ts @@ -9,12 +9,15 @@ 'use strict' import * as vscode from 'vscode'; +import * as path from 'path'; +import * as fs from 'fs'; import * as log from './log'; import * as msg from './messages'; import {DisposableLikeList, onReject} from './functions'; import {Project, ProjectEngine, ProjectContentProvider, ProjectContentProviderState} from './project'; import server from './tools'; +import simpleGit, { SimpleGit } from 'simple-git' /** * Register transformation command. @@ -52,8 +55,35 @@ export function registerCommands( }).then((value) => {vscode.commands.executeCommand('tsar.stop', project.uri)}); state.active = true; project.focus = state; + + let git = simpleGit(path.dirname(project.uri.fsPath)); + let isRepo = await git.checkIsRepo() + if (!isRepo) { + await git.init(); + } + let GitFilePath = path.join(path.dirname(project.uri.fsPath), '.tsar_git'); + if (!fs.existsSync(GitFilePath)) { + fs.writeFileSync(GitFilePath, '', 'utf-8'); // here can be written information that should be saved for git + } + + let isIgnored = await git.checkIgnore(project.uri.fsPath) + if (isIgnored.some((path) => path == project.uri.fsPath)){ + throw new Error(log.Error.gitIgnore.replace('{0}', project.uri.fsPath)); + } else { + await git.add(project.uri.fsPath); + await git.commit(`${path.basename(project.uri.fsPath)} before sapfor transformation ${info.title}`, project.uri.fsPath, {"--author": "Tsar-advisor "}); + } + await engine.runTool(project, info.run) project.send(''); + + isIgnored = await git.checkIgnore(project.uri.fsPath); + if (isIgnored.some((path) => path == project.uri.fsPath)){ + throw new Error(log.Error.gitIgnore.replace('{0}', project.uri.fsPath)); + } else { + await git.add(project.uri.fsPath); + await git.commit(`${path.basename(project.uri.fsPath)} after sapfor transformation ${info.title}`, project.uri.fsPath, {"--author": "Tsar-advisor "}); + } }, reason => { return onReject(reason, uri) }) })) diff --git a/tsconfig.json b/tsconfig.json index 11282c9..ba4c969 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,8 @@ "target": "es6", "outDir": "out", "lib": [ - "es6" + "es6", + "DOM" ], "sourceMap": true, "rootDir": "." From ea011f12dd43e309eedc0a03624f4855fabb81b4 Mon Sep 17 00:00:00 2001 From: Maria Opryshko Date: Sat, 25 May 2024 12:44:40 -0400 Subject: [PATCH 2/5] Add visualization --- package-lock.json | 56 ++--- package.json | 17 +- src/extension.ts | 554 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 597 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 22c7574..c5adf85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -343,9 +343,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "inflight": { @@ -462,9 +462,9 @@ "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -508,6 +508,14 @@ "requires": { "is-core-module": "^2.1.0", "path-parse": "^1.0.6" + }, + "dependencies": { + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + } } } } @@ -530,18 +538,18 @@ }, "dependencies": { "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "shell-quote": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", - "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", "dev": true } } @@ -601,12 +609,6 @@ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -675,9 +677,9 @@ } }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -686,9 +688,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "shallow-copy": { @@ -835,9 +837,9 @@ "dev": true }, "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", "dev": true }, "validate-npm-package-license": { diff --git a/package.json b/package.json index 9490ff9..df969f7 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "onCommand:tsar.transform.rename", "onCommand:tsar.parallel.openmp", "onCommand:tsar.parallel.dvmh", - "onCommand:tsar.parallel.dvmhsm" + "onCommand:tsar.parallel.dvmhsm", + "onCommand:tsar.gitgraph" ], "main": "./out/src/extension", "contributes": { @@ -110,6 +111,10 @@ "command": "tsar.start", "title": "TSAR Analyze" }, + { + "command": "tsar.gitgraph", + "title": "TSAR Graph" + }, { "command": "tsar.stop", "title": "TSAR Close session", @@ -268,6 +273,11 @@ "submenu": "tsar.submenu.parallel", "when": "resourceLangId == 'c' || resourceLangId == 'cpp'", "group": "1_tsar" + }, + { + "command": "tsar.gitgraph", + "when": "resourceLangId == 'c' || resourceLangId == 'cpp'", + "group": "1_tsar" } ], "explorer/context": [ @@ -290,6 +300,11 @@ "submenu": "tsar.submenu.parallel", "when": "resourceLangId == 'c' || resourceLangId == 'cpp'", "group": "1_tsar" + }, + { + "command": "tsar.gitgraph", + "when": "resourceLangId == 'c' || resourceLangId == 'cpp'", + "group": "1_tsar" } ], "tsar.submenu.tfm": [ diff --git a/src/extension.ts b/src/extension.ts index e7e0076..1395264 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -16,7 +16,7 @@ import * as lt from './loopTree'; import * as at from './aliasTree'; import * as msg from './messages'; import {onReject} from './functions'; -import {ProjectEngine, Project } from './project'; +import {ProjectEngine, Project} from './project'; import {ProjectProvider} from './general'; import {CalleeFuncProvider, CalleeFuncProviderState} from './calleeFunc'; import * as t from './transformProvider'; @@ -24,6 +24,8 @@ import server from './tools'; import { FileListProvider } from './fileList'; import { LoopTreeViewProvider } from './loopExplorer'; import simpleGit, { SimpleGit } from 'simple-git' +import {exec, execSync} from 'child_process' + /** * Open log file (log.Extension.log), returns true on success. @@ -56,6 +58,501 @@ function openLog(): boolean { return true; } +/* +Matches commit hashes to the column number +Return value: [{ hash: string, + children: string[], + height: number}] +*/ +async function getCommitGraph(repositoryPath: string) { + let commits = await getCommitsChildren(repositoryPath); + + const orderedCommits = getSortedCommits(repositoryPath); + const hashIdx: Map = new Map(); + + orderedCommits.forEach((value, index) => { + hashIdx.set(value.hash, index); + }); + + const height: number[] = Array(Object.keys(orderedCommits).length).fill(0); + const subTree: string[] = []; + + function dfs(commit_hash: string) { + if(commits[commit_hash].length == 0 || height[hashIdx.get(commit_hash)] != 0){ + subTree.push(commit_hash); + let newHeight = 0; + for(let idx = hashIdx.get(subTree[0]); idx <= hashIdx.get(subTree[subTree.length - 1]); idx++){ + if(height[idx] > newHeight){ + newHeight = height[idx]; + } + } + + for(let idx = 1; idx <= subTree.length - 2; idx++){ + height[hashIdx.get(subTree[idx])] = newHeight +1; + } + + if(commits[commit_hash].length == 0 && height[hashIdx.get(commit_hash)] == 0){ + height[hashIdx.get(commit_hash)] = newHeight + 1; + } + + subTree.length = 0; + + return; + } + + for (const neighbor of commits[commit_hash]) { + subTree.push(commit_hash); + dfs(neighbor); + } + } + + dfs(orderedCommits[0].hash); + height[0] = 1; + + let answer = []; + for(const commit of orderedCommits){ + answer.push({ + hash: commit.hash, + children: commits[commit.hash], + height: height[hashIdx.get(commit.hash)] + }); + } + return answer; +} + + +function getSortedCommits(repositoryPath: string): { hash: string; date: number}[] { + const gitLogOutput = execSync('git log --pretty=format:"%H %at" --all', { cwd: repositoryPath }).toString(); + + const lines = gitLogOutput.split('\n'); + + const commits = lines.map(line => { + const [hash, dateString] = line.split(' '); + return { + hash, + date: parseInt(dateString) + }; + }); + + commits.sort((a, b) => a.date - b.date); + + return commits; +} + +async function getCommitsChildren(repositoryPath: string): Promise<{ [key: string]: string[] } | null> { + return new Promise((resolve, reject) => { + exec('git rev-list --all --children', { cwd: repositoryPath }, (error, stdout, stderr) => { + if (error) { + console.error('Error executing git log:', stderr); + reject(null); + return; + } + + const commitGraph: { [key: string]: string[] } = {}; + const lines = stdout.trim().split('\n'); + + lines.forEach(line => { + const [commitHash, ...parentHashes] = line.split(' '); + commitGraph[commitHash] = parentHashes; + }); + + resolve(commitGraph); + }); + }); +} + + +function getHEADCommit(repositoryPath: string): string { + return execSync('git rev-parse HEAD', { cwd: repositoryPath }).toString().trim(); +} + + +function getCommitsData(repositoryPath: string): { + hash: string, + authorName: string, + authorEmail: string, + authorDate: string, + message: string}[] { + const gitLogOutput = execSync('git log --format="%H%n%an%n%ae%n%ai%n%s" --all', { cwd: repositoryPath }).toString(); + + const lines = gitLogOutput.split('\n'); + let commits = []; + + for (let i = 0; i < lines.length - 1; i += 5) { + commits.push({ + hash: lines[i], + authorName: lines[i + 1], + authorEmail: lines[i + 2], + authorDate: lines[i + 3].slice(0, -6), + message: lines[i + 4] + }); + } + + return commits; +} + +function getGraphWebviewContent(commitGraph, HEAD, commitsData) { + const commitGraphString = JSON.stringify(commitGraph); + const commitsDateString = JSON.stringify(commitsData); + + const x = 210; // Initial x position of git graph + const y = 50; // Initial y position of git graph + const xOffset = 100; // Horizontal distance between nodes + const yOffset = 100; // Vertical distance between nodes + const textWidth = 500; // Width of column with commit message + + let max_height = 1; // Width of graph + + commitGraph.forEach((commit)=> { + if (commit.height > max_height) { + max_height = commit.height; + } + }); + + return ` + + + Git Graph + + + + +
+
+ +
+
+

Commit Details

+

Hash:

+

Message:

+

Author Name:

+

Author Email:

+

Date:

+
+
+ + + `; +} + + + export function activate(context: vscode.ExtensionContext) { if (!openLog()) return; @@ -77,6 +574,58 @@ export function activate(context: vscode.ExtensionContext) { [t.TransformationProvider.scheme, new t.TransformationProvider], [at.AliasTreeProvider.scheme, new at.AliasTreeProvider] ); + + let gitGraph = vscode.commands.registerCommand('tsar.gitgraph', async (uri: vscode.Uri) => { + const repositoryPath = path.dirname(uri.fsPath); + + const GitFilePath = path.join(path.dirname(uri.fsPath), '.tsar_git'); + if (!fs.existsSync(GitFilePath)) { + fs.writeFileSync(GitFilePath, '', 'utf-8'); // here can be written information that should be saved for git + } + + let git = simpleGit(repositoryPath); + git.checkIsRepo() + .then(async (isRepo) => { + + if (!isRepo) { + await git.init(); + + } else { + const panel = vscode.window.createWebviewPanel( + 'commitGraph', + 'Commit Graph', + vscode.ViewColumn.One, + { + enableScripts: true + } + ); + + const graph = await getCommitGraph(repositoryPath); + const HEADCommit = getHEADCommit(repositoryPath); + const commitsData = getCommitsData(repositoryPath); + + panel.webview.html = getGraphWebviewContent(graph, HEADCommit, commitsData); + + panel.webview.onDidReceiveMessage( + async message => { + switch (message.command) { + case 'gitCheckout': + try { + await git.checkout(message.commitHash); + panel.webview.postMessage({ command: 'gitCheckout', commitHash: message.commitHash }) + } catch(err){ + vscode.window.showErrorMessage(err.message, "Close"); + } + break; + } + }, + undefined, + context.subscriptions + ); + } + }) + }); + let start = vscode.commands.registerCommand( 'tsar.start', (uri:vscode.Uri) => { vscode.workspace.openTextDocument(uri) @@ -98,6 +647,7 @@ export function activate(context: vscode.ExtensionContext) { await git.init(); } }) + let GitFilePath = path.join(path.dirname(project.uri.fsPath), '.tsar_git'); if (!fs.existsSync(GitFilePath)) { fs.writeFileSync(GitFilePath, '', 'utf-8'); // here can be written information that should be saved for git @@ -214,5 +764,5 @@ export function activate(context: vscode.ExtensionContext) { project.focus = state; project.send(request); }); - context.subscriptions.push(start, stop, statistic, openProject, showCalleeFunc); + context.subscriptions.push(start, stop, statistic, openProject, showCalleeFunc, gitGraph); } From 6265b9855d890674d191770c3f562c359627fee0 Mon Sep 17 00:00:00 2001 From: Maria Opryshko Date: Thu, 21 Nov 2024 15:48:24 +0300 Subject: [PATCH 3/5] auto generation of Compilation Database --- package.json | 14 ++++++++++++++ src/extension.ts | 39 ++++++++++++++++++++++++++++++++++++++- src/project.ts | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index df969f7..812cc60 100644 --- a/package.json +++ b/package.json @@ -194,6 +194,10 @@ "command": "tsar.call.graph.unsafe", "title": "View Unsafe Control Flow", "icon": "icons/call-graph.svg" + }, + { + "command": "tsar.createCompilationDatabase", + "title": "TSAR Refactor Compilation Database" } ], "menus": { @@ -278,6 +282,11 @@ "command": "tsar.gitgraph", "when": "resourceLangId == 'c' || resourceLangId == 'cpp'", "group": "1_tsar" + }, + { + "command": "tsar.createCompilationDatabase", + "when": "resourceLangId == 'c' || resourceLangId == 'cpp'", + "group": "1_tsar" } ], "explorer/context": [ @@ -305,6 +314,11 @@ "command": "tsar.gitgraph", "when": "resourceLangId == 'c' || resourceLangId == 'cpp'", "group": "1_tsar" + }, + { + "command": "tsar.createCompilationDatabase", + "when": "resourceLangId == 'c' || resourceLangId == 'cpp'", + "group": "1_tsar" } ], "tsar.submenu.tfm": [ diff --git a/src/extension.ts b/src/extension.ts index 1395264..45f38cd 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -626,6 +626,43 @@ export function activate(context: vscode.ExtensionContext) { }) }); + let compilationDatabase = vscode.commands.registerCommand( + 'tsar.createCompilationDatabase', async () => { + let selected; + + //should be selected at least one file + while (!selected || selected.length === 0) { + const files = await vscode.workspace.findFiles('**/*.{c,cpp}'); + + const items = files.map(file => ({ + label: path.basename(file.fsPath), + description: file.fsPath + })); + + selected = await vscode.window.showQuickPick(items, { + canPickMany: true, + placeHolder: 'Choose files for compilation (.c or .cpp)' + }); + + if (!selected || selected.length === 0) { + vscode.window.showWarningMessage('You must select at least one file!'); + } + } + + const compileEntries = selected.map(item => ({ + directory: path.dirname(item.description), + file: item.label, + command: "tsar -emit-llvm " + item.description + })); + + const compileCommandsPath = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, 'compile_commands.json'); + + fs.writeFileSync(compileCommandsPath, JSON.stringify(compileEntries, null, 2)); + + vscode.window.showInformationMessage(`compile_commands.json is created at: ${compileCommandsPath}`); + } + ); + let start = vscode.commands.registerCommand( 'tsar.start', (uri:vscode.Uri) => { vscode.workspace.openTextDocument(uri) @@ -764,5 +801,5 @@ export function activate(context: vscode.ExtensionContext) { project.focus = state; project.send(request); }); - context.subscriptions.push(start, stop, statistic, openProject, showCalleeFunc, gitGraph); + context.subscriptions.push(start, stop, statistic, openProject, showCalleeFunc, gitGraph, compilationDatabase); } diff --git a/src/project.ts b/src/project.ts index edf203f..3101ef4 100644 --- a/src/project.ts +++ b/src/project.ts @@ -177,8 +177,33 @@ export class ProjectEngine { * TODO (kaniandr@gmail.com): currently each project consists of a single * file, update to support projects configured with a help of *.json file. */ - start(doc: vscode.TextDocument, tool:ToolT): Thenable { - return new Promise((resolve, reject) => { + start(doc: vscode.TextDocument, tool:ToolT): Thenable { + return new Promise(async(resolve, reject) => { + let compileCommandsPath = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, 'compile_commands.json'); + + if (!fs.existsSync(compileCommandsPath)) { + await vscode.commands.executeCommand('tsar.createCompilationDatabase') + } + const data = await fs.promises.readFile(compileCommandsPath, 'utf-8'); + const json = JSON.parse(data); + let fileList = json.map((entry: { directory: string, file: string }) => entry.directory + "/" + entry.file).join(" "); + + let command = "tsar -emit-llvm -build-path=" + path.dirname(doc.uri.fsPath) + " " + fileList; + + // how send message, if class Project(who send messages) has constructor which need uri of a project, but tota.ll file is not created yet? + // let message = new msg.CommandLine(command); + // project.send(message); + + child_process.execSync(command); + + const compiledFiles = await fileList.replace(/\.(c|cpp)\b/g, '.ll'); + const totalFilePath = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, 'total.ll'); + + child_process.execSync("llvm-link-15 -S " + compiledFiles + " -o " + totalFilePath); + + let projectFileUri = vscode.Uri.file(totalFilePath); + doc = await vscode.workspace.openTextDocument(projectFileUri); + let project = this.project(doc.uri); if (project !== undefined) { vscode.window.showWarningMessage( @@ -194,9 +219,9 @@ export class ProjectEngine { state.provider.update(project); return undefined; } - let check = this._checkDocument(doc); - if (check) - return reject(check); + let check = this._checkDocument(doc); + // if (check) + // return reject(check); let uri = doc.uri; let prjDir = this._makeProjectDir(path.dirname(uri.fsPath)); if (typeof prjDir != 'string') From 7ba70da02c5be33726edb18811c328ecfbc9085d Mon Sep 17 00:00:00 2001 From: Maria Opryshko Date: Thu, 19 Dec 2024 01:16:55 +0300 Subject: [PATCH 4/5] TSAR project can be multi file --- package.json | 21 +++-- src/extension.ts | 225 ++++++++++++++++++++++++++++++++++++++--------- src/project.ts | 111 +++++++++++++++++------ src/tools.ts | 3 + 4 files changed, 289 insertions(+), 71 deletions(-) diff --git a/package.json b/package.json index 812cc60..50dec7b 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,9 @@ "onCommand:tsar.parallel.openmp", "onCommand:tsar.parallel.dvmh", "onCommand:tsar.parallel.dvmhsm", - "onCommand:tsar.gitgraph" + "onCommand:tsar.gitgraph", + "onCommand:tsar.refactorTsarProject", + "onCommand:tsar.addToTsarProject" ], "main": "./out/src/extension", "contributes": { @@ -196,8 +198,12 @@ "icon": "icons/call-graph.svg" }, { - "command": "tsar.createCompilationDatabase", - "title": "TSAR Refactor Compilation Database" + "command": "tsar.refactorTsarProject", + "title": "TSAR Refactor TSAR Project" + }, + { + "command": "tsar.addToTsarProject", + "title": "TSAR Add to Project" } ], "menus": { @@ -284,7 +290,7 @@ "group": "1_tsar" }, { - "command": "tsar.createCompilationDatabase", + "command": "tsar.refactorTsarProject", "when": "resourceLangId == 'c' || resourceLangId == 'cpp'", "group": "1_tsar" } @@ -316,7 +322,12 @@ "group": "1_tsar" }, { - "command": "tsar.createCompilationDatabase", + "command": "tsar.refactorTsarProject", + "when": "resourceLangId == 'c' || resourceLangId == 'cpp'", + "group": "1_tsar" + }, + { + "command": "tsar.addToTsarProject", "when": "resourceLangId == 'c' || resourceLangId == 'cpp'", "group": "1_tsar" } diff --git a/src/extension.ts b/src/extension.ts index 45f38cd..c6a25ae 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -25,6 +25,7 @@ import { FileListProvider } from './fileList'; import { LoopTreeViewProvider } from './loopExplorer'; import simpleGit, { SimpleGit } from 'simple-git' import {exec, execSync} from 'child_process' +import * as child_process from 'child_process'; /** @@ -552,6 +553,106 @@ function getGraphWebviewContent(commitGraph, HEAD, commitsData) { } +function getWorkspaceRoot(fileUri: vscode.Uri) { + /// Получаем все открытые рабочие папки + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders || workspaceFolders.length === 0) { + vscode.window.showErrorMessage('No workspace folder is open.'); + return undefined; + } + + // Ищем в какой рабочей области находится файл + const containingFolder = workspaceFolders.find(folder => + fileUri.fsPath.startsWith(folder.uri.fsPath) + ); + + if (!containingFolder) { + vscode.window.showErrorMessage('The file is not part of any open workspace folder.'); + return undefined; + } + + return containingFolder.uri.fsPath; +} + + +function addFileToCompileCommands(filePath: string) { + const fileUri = vscode.Uri.file(filePath); + const workspaceRoot = getWorkspaceRoot(fileUri); + if (!workspaceRoot) { + return; + } + + const compileCommandsPath = path.join(workspaceRoot, 'compile_commands.json'); + + // Создаем файл, если он не существует + if (!fs.existsSync(compileCommandsPath)) { + fs.writeFileSync(compileCommandsPath, '[]', 'utf8'); + } + + let data = JSON.parse(fs.readFileSync(compileCommandsPath, 'utf8')); + + const absoluteFilePath = path.isAbsolute(filePath) + ? filePath + : path.join(workspaceRoot, filePath); + + // Проверяем, существует ли запись для файла + const fileAlreadyExists = data.some((entry: any) => { + // Преобразуем путь файла в абсолютный, если он относительный + const normalizedEntryPath = path.isAbsolute(entry.file) + ? path.normalize(entry.file) + : path.join(entry.directory, path.normalize(entry.file)); + + return normalizedEntryPath === absoluteFilePath; + }); + + if (fileAlreadyExists) { + vscode.window.showInformationMessage(`${path.basename(filePath)} already in CompilaionDatabase.`); + return; + } + + // Добавляем новую запись + data.push({ + directory: workspaceRoot, + file: absoluteFilePath, + command: '' + }); + + fs.writeFileSync(compileCommandsPath, JSON.stringify(data, null, 2), 'utf8'); + vscode.window.showInformationMessage(`${path.basename(filePath)} has been added to compile_commands.json.`); +} + +function addFileToTsarProject(filePath: string) { + const fileUri = vscode.Uri.file(filePath); + const workspaceRoot = getWorkspaceRoot(fileUri); + if (!workspaceRoot) { + return; + } + + const tsarProjectPath = path.join(workspaceRoot, 'tsar_project_file.json'); + + if (!fs.existsSync(tsarProjectPath)) { + fs.writeFileSync(tsarProjectPath, '[]', 'utf8'); + } + + let data = JSON.parse(fs.readFileSync(tsarProjectPath, 'utf8')); + + const absoluteFilePath = path.isAbsolute(filePath) + ? filePath + : path.join(workspaceRoot, filePath); + + if (data.includes(absoluteFilePath)) { + vscode.window.showInformationMessage(`${path.basename(filePath)} already in Tsar Project.`); + return; + } + + data.push(absoluteFilePath); + + // Сохраняем обновленный массив обратно в файл + fs.writeFileSync(tsarProjectPath, JSON.stringify(data, null, 2), 'utf8'); + vscode.window.showInformationMessage(`${path.basename(filePath)} has been added in Tsar Projet File.`); + +} + export function activate(context: vscode.ExtensionContext) { if (!openLog()) @@ -626,11 +727,12 @@ export function activate(context: vscode.ExtensionContext) { }) }); - let compilationDatabase = vscode.commands.registerCommand( - 'tsar.createCompilationDatabase', async () => { + let refactorProject = vscode.commands.registerCommand( + 'tsar.refactorTsarProject', async (uri:vscode.Uri) => { + // Пользователь должен выбрать, какие файлы должны составлять проект let selected; - //should be selected at least one file + // Должен быть выбран хотя бы один файл while (!selected || selected.length === 0) { const files = await vscode.workspace.findFiles('**/*.{c,cpp}'); @@ -649,49 +751,90 @@ export function activate(context: vscode.ExtensionContext) { } } - const compileEntries = selected.map(item => ({ - directory: path.dirname(item.description), - file: item.label, - command: "tsar -emit-llvm " + item.description - })); - - const compileCommandsPath = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, 'compile_commands.json'); + // добавление файлов в Compilation Database + selected.forEach((item) => { + const filePath = item.description; + addFileToCompileCommands(filePath); + }); - fs.writeFileSync(compileCommandsPath, JSON.stringify(compileEntries, null, 2)); + const absolutePaths = selected.map(item => item.description); + + const tsarProjectPath = path.join(getWorkspaceRoot(uri), 'tsar_project_file.json'); - vscode.window.showInformationMessage(`compile_commands.json is created at: ${compileCommandsPath}`); + // создание или преписывание TSAR файла-проекта + fs.writeFileSync(tsarProjectPath, JSON.stringify(absolutePaths, null, 2)); } - ); + ) + + let addFileToProject = vscode.commands.registerCommand( + 'tsar.addToTsarProject', async (uri:vscode.Uri) => { + addFileToCompileCommands(uri.fsPath); + addFileToTsarProject(uri.fsPath); + + }); let start = vscode.commands.registerCommand( - 'tsar.start', (uri:vscode.Uri) => { - vscode.workspace.openTextDocument(uri) - .then((success) => { - return engine.start(success, - server.tools.find(t=>{return t.name === 'tsar'})); - }) - .then( - async project => { - await engine.runTool(project); - project.providerState(FileListProvider.scheme).active = true; - project.send(new msg.FileList); - vscode.commands.executeCommand('tsar.function.list', project.uri); - - let git = simpleGit(path.dirname(project.uri.fsPath)); - git.checkIsRepo() - .then(async (isRepo) => { - if (!isRepo) { - await git.init(); - } - }) + 'tsar.start', async (uri:vscode.Uri) => { + const projectDir = getWorkspaceRoot(uri).toString(); + const projectPath = path.join(projectDir, 'tsar_project_file.json'); + + if (!fs.existsSync(projectPath)) { + await vscode.commands.executeCommand('tsar.refactorTsarProject') + } + vscode.workspace.openTextDocument(vscode.Uri.file(projectPath)) + .then((success) => { + return engine.start(success, + server.tools.find(t=>{return t.name === 'project'})); + }) + .then( async project => { + project = await engine.runProjectTool(project); + const data = fs.readFileSync(projectPath, 'utf-8'); + const fileList = JSON.parse(data); + const compiledFiles = fileList.toString().replace(',', ' ').replace(/\.(c|cpp)\b/g, '.ll'); + const totalFilePath = path.join(projectDir, 'total.ll'); + + try { + const cliString = "llvm-link-15 -S " + compiledFiles + " -o " + totalFilePath + child_process.execSync(cliString); + } catch (error) { + const errorMessage = `Linking failed: ${error.stderr.toString()}`; + vscode.window.showErrorMessage(errorMessage); + } - let GitFilePath = path.join(path.dirname(project.uri.fsPath), '.tsar_git'); - if (!fs.existsSync(GitFilePath)) { - fs.writeFileSync(GitFilePath, '', 'utf-8'); // here can be written information that should be saved for git + let projectFileUri = vscode.Uri.file(totalFilePath) + return projectFileUri; + }) + .then((projectUri) => { + return vscode.workspace.openTextDocument(projectUri) + }) + .then((success) => { + return engine.start(success, + server.tools.find(t=>{return t.name === 'tsar'})); + }) + .then( + async project => { + await engine.runTool(project); + project.providerState(FileListProvider.scheme).active = true; + project.send(new msg.FileList); + vscode.commands.executeCommand('tsar.function.list', project.uri); + + let git = simpleGit(path.dirname(project.uri.fsPath)); + git.checkIsRepo() + .then(async (isRepo) => { + if (!isRepo) { + await git.init(); } - }, - reason => { onReject(reason, uri) }) - }); + }) + + let GitFilePath = path.join(path.dirname(project.uri.fsPath), '.tsar_git'); + if (!fs.existsSync(GitFilePath)) { + fs.writeFileSync(GitFilePath, '', 'utf-8'); // here can be written information that should be saved for git + } + }, + reason => { onReject(reason, uri) + }) + } + ); t.registerCommands([ { command: 'tsar.transform.propagate', @@ -704,7 +847,7 @@ export function activate(context: vscode.ExtensionContext) { run: '-clang-inline' }, { - command: 'tsar.transform.replace', + command: 'tsar.transformccff.replace', title: 'TSAR Structure Replacement', run: '-clang-struct-replacement' }, @@ -801,5 +944,5 @@ export function activate(context: vscode.ExtensionContext) { project.focus = state; project.send(request); }); - context.subscriptions.push(start, stop, statistic, openProject, showCalleeFunc, gitGraph, compilationDatabase); + context.subscriptions.push(start, stop, statistic, openProject, showCalleeFunc, gitGraph, refactorProject, addFileToProject); } diff --git a/src/project.ts b/src/project.ts index 3101ef4..76e89a7 100644 --- a/src/project.ts +++ b/src/project.ts @@ -44,7 +44,7 @@ export class ProjectEngine { * Build internal identifier for a specified project uri. */ private static _projectID(uri: vscode.Uri): string { - return vscode.Uri.file(uri.path).toString(); + return vscode.Uri.file(uri.path).fsPath.toString(); } /** @@ -178,31 +178,38 @@ export class ProjectEngine { * file, update to support projects configured with a help of *.json file. */ start(doc: vscode.TextDocument, tool:ToolT): Thenable { - return new Promise(async(resolve, reject) => { - let compileCommandsPath = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, 'compile_commands.json'); + return new Promise((resolve, reject) => { + // //не забыть поменять на правильный выбор директории + // let projectPath = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, 'tsar_project_file.json'); - if (!fs.existsSync(compileCommandsPath)) { - await vscode.commands.executeCommand('tsar.createCompilationDatabase') - } - const data = await fs.promises.readFile(compileCommandsPath, 'utf-8'); - const json = JSON.parse(data); - let fileList = json.map((entry: { directory: string, file: string }) => entry.directory + "/" + entry.file).join(" "); + // if (!fs.existsSync(projectPath)) { + // await vscode.commands.executeCommand('tsar.refactorTsarProject') + // } + // const data = await fs.promises.readFile(projectPath, 'utf-8'); + // const fileList = JSON.parse(data); + // const fileListStrFormat = fileList.toString().replace(',', ' ') + // // let fileList = json.map(filepath => filepath.join(" ")); - let command = "tsar -emit-llvm -build-path=" + path.dirname(doc.uri.fsPath) + " " + fileList; + // let command = "tsar -emit-llvm -build-path=" + vscode.workspace.workspaceFolders[0].uri.fsPath + " " + fileListStrFormat; - // how send message, if class Project(who send messages) has constructor which need uri of a project, but tota.ll file is not created yet? - // let message = new msg.CommandLine(command); - // project.send(message); + // // how send message, if class Project(who send messages) has constructor which need uri of a project, but tota.ll file is not created yet? + // // let message = new msg.CommandLine(command); + // // project.send(message); - child_process.execSync(command); + // child_process.execSync(command); - const compiledFiles = await fileList.replace(/\.(c|cpp)\b/g, '.ll'); - const totalFilePath = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, 'total.ll'); + // const compiledFiles = await fileListStrFormat.replace(/\.(c|cpp)\b/g, '.ll'); + // const totalFilePath = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, 'total.ll'); - child_process.execSync("llvm-link-15 -S " + compiledFiles + " -o " + totalFilePath); + // try { + // child_process.execSync("llvm-link-15 -S " + compiledFiles + " -o " + totalFilePath); + // } catch (error) { + // const errorMessage = `Linking failed: ${error.stderr.toString()}`; + // vscode.window.showErrorMessage(errorMessage); + // } - let projectFileUri = vscode.Uri.file(totalFilePath); - doc = await vscode.workspace.openTextDocument(projectFileUri); + // let projectFileUri = vscode.Uri.file(totalFilePath); + // doc = await vscode.workspace.openTextDocument(projectFileUri); let project = this.project(doc.uri); if (project !== undefined) { @@ -226,7 +233,7 @@ export class ProjectEngine { let prjDir = this._makeProjectDir(path.dirname(uri.fsPath)); if (typeof prjDir != 'string') return reject(prjDir); - this._startServer(uri, prjDir, tool, this._environment, resolve, reject); + this._startServer(uri, prjDir, tool, this._environment, resolve, reject, doc); return undefined; }) } @@ -302,6 +309,29 @@ export class ProjectEngine { return project; } + // project should be *.json file + async runProjectTool(project: Project, query?: string) { + let cl = new msg.CommandLine(log.Extension.displayName); + + this._projects.forEach((val, key) =>{ + cl.Args.push(vscode.Uri.file(key).fsPath); + }) + cl.Args.push("-emit-llvm"); + cl.Query = "-emit-llvm"; + // cl.Args.push(query); + + project.arguments = cl.Args; + + // if (query) + // cl.Query = query; + cl.Output = path.join(project.dirname, log.Project.output); + cl.Error = path.join(project.dirname, log.Project.error); + + await project.send(cl); + + return project; + } + /** * Stop analysis of a specified project. */ @@ -364,7 +394,7 @@ export class ProjectEngine { * execution. */ private _startServer(uri: vscode.Uri, prjDir: string, - tool: ToolT, env: any, resolve: any, reject: any) { + tool: ToolT, env: any, resolve: any, reject: any, doc) { let server: child_process.ChildProcess; let userConfig = vscode.workspace.getConfiguration(log.Extension.id); let pathToServer = which.sync( @@ -390,7 +420,7 @@ export class ProjectEngine { // do not move project inside 'data/message' event listener // it must be shared between all messages evaluation let project: Project; - let onServerData = (raw: string) => { + let onServerData = async (raw: string) => { let client: net.Socket; try { let data = JSON.parse(raw); @@ -414,7 +444,17 @@ export class ProjectEngine { this, this._context.subscriptions); project.register(scheme, provider.state()); } - this._projects.set(ProjectEngine._projectID(uri), project); + if (path.extname(uri.fsPath).toLowerCase() === '.json') { + const data = fs.readFileSync(uri.fsPath, 'utf-8'); + const fileList = JSON.parse(data); + for (const file of fileList) { + this._projects.set(ProjectEngine._projectID(vscode.Uri.file(file)), project); + } + + + } else { + this._projects.set(ProjectEngine._projectID(uri), project); + } client.on('error', (err) => {this._internalError(err)}); client.on('data', (data:string) => { log.Log.logs[0].write(log.Message.server.replace('{0}', data)); @@ -703,13 +743,34 @@ export class Project { /** * Send request to a server. */ - send(request: any) { + send(request: any): Promise { + return new Promise((resolve, reject) => { let requestString = JSON.stringify(request) + log.Project.delimiter; log.Log.logs[0].write(log.Message.client.replace('{0}', requestString)); this._client.write(requestString) + + this._client.on('data', (data) => { + resolve(); + }); + this._client.on('error', (error) => { + reject(error); + }); + + this._client.on('end', () => { + reject(); + }); + + // this._client.on('data', (data) => { + // const response = data.toString(); + // console.log('Ответ от сервера:', response); + // }); + + // this._client.on('end', () => { + // console.log('Соединение с сервером закрыто.'); + // }); /*if (!this._client.write(requestString)) this._client.once('drain', () => {this.send(request)});*/ - } + })} /** * Register content provider with a specified base scheme. diff --git a/src/tools.ts b/src/tools.ts index 508641d..0dc055a 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -97,6 +97,9 @@ export default target: '-no-format' } ] + }, + { + name: "project" } ] } \ No newline at end of file From 3df7b04e056663a0fcf02fc95d7e4f291a466d92 Mon Sep 17 00:00:00 2001 From: Maria Opryshko Date: Thu, 19 Dec 2024 13:45:15 +0300 Subject: [PATCH 5/5] Fixed: wait for compilation from server --- src/extension.ts | 57 +++++++++++++++++++++++++++---------------- src/project.ts | 63 ++++-------------------------------------------- 2 files changed, 41 insertions(+), 79 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index c6a25ae..5b61809 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -552,6 +552,25 @@ function getGraphWebviewContent(commitGraph, HEAD, commitsData) { `; } +async function waitForFiles(filePaths: string[], timeout: number = 10000) { + const checkInterval = 100; // проверять каждые 100 мс + let elapsedTime = 0; + + return new Promise((resolve, reject) => { + const interval = setInterval(() => { + const allFilesExist = filePaths.every(file => fs.existsSync(file)); + if (allFilesExist) { + clearInterval(interval); + resolve(); + } + elapsedTime += checkInterval; + if (elapsedTime >= timeout) { + clearInterval(interval); + reject(new Error(`Files were not created in ${timeout} ms: ${filePaths.join(', ')}`)); + } + }, checkInterval); + }); +} function getWorkspaceRoot(fileUri: vscode.Uri) { /// Получаем все открытые рабочие папки @@ -571,7 +590,7 @@ function getWorkspaceRoot(fileUri: vscode.Uri) { return undefined; } - return containingFolder.uri.fsPath; + return containingFolder.uri.fsPath.toString(); } @@ -605,20 +624,16 @@ function addFileToCompileCommands(filePath: string) { return normalizedEntryPath === absoluteFilePath; }); - if (fileAlreadyExists) { - vscode.window.showInformationMessage(`${path.basename(filePath)} already in CompilaionDatabase.`); - return; + if (!fileAlreadyExists) { + // Добавляем новую запись + data.push({ + directory: workspaceRoot, + file: absoluteFilePath, + command: '' + }); + + fs.writeFileSync(compileCommandsPath, JSON.stringify(data, null, 2), 'utf8'); } - - // Добавляем новую запись - data.push({ - directory: workspaceRoot, - file: absoluteFilePath, - command: '' - }); - - fs.writeFileSync(compileCommandsPath, JSON.stringify(data, null, 2), 'utf8'); - vscode.window.showInformationMessage(`${path.basename(filePath)} has been added to compile_commands.json.`); } function addFileToTsarProject(filePath: string) { @@ -641,7 +656,6 @@ function addFileToTsarProject(filePath: string) { : path.join(workspaceRoot, filePath); if (data.includes(absoluteFilePath)) { - vscode.window.showInformationMessage(`${path.basename(filePath)} already in Tsar Project.`); return; } @@ -649,8 +663,6 @@ function addFileToTsarProject(filePath: string) { // Сохраняем обновленный массив обратно в файл fs.writeFileSync(tsarProjectPath, JSON.stringify(data, null, 2), 'utf8'); - vscode.window.showInformationMessage(`${path.basename(filePath)} has been added in Tsar Projet File.`); - } @@ -758,8 +770,9 @@ export function activate(context: vscode.ExtensionContext) { }); const absolutePaths = selected.map(item => item.description); + const workspaceRoot = getWorkspaceRoot(uri); - const tsarProjectPath = path.join(getWorkspaceRoot(uri), 'tsar_project_file.json'); + const tsarProjectPath = path.join(workspaceRoot, 'tsar_project_file.json'); // создание или преписывание TSAR файла-проекта fs.writeFileSync(tsarProjectPath, JSON.stringify(absolutePaths, null, 2)); @@ -775,11 +788,11 @@ export function activate(context: vscode.ExtensionContext) { let start = vscode.commands.registerCommand( 'tsar.start', async (uri:vscode.Uri) => { - const projectDir = getWorkspaceRoot(uri).toString(); + const projectDir = getWorkspaceRoot(uri); const projectPath = path.join(projectDir, 'tsar_project_file.json'); if (!fs.existsSync(projectPath)) { - await vscode.commands.executeCommand('tsar.refactorTsarProject') + await vscode.commands.executeCommand('tsar.refactorTsarProject', uri); } vscode.workspace.openTextDocument(vscode.Uri.file(projectPath)) .then((success) => { @@ -787,12 +800,14 @@ export function activate(context: vscode.ExtensionContext) { server.tools.find(t=>{return t.name === 'project'})); }) .then( async project => { - project = await engine.runProjectTool(project); + project = engine.runProjectTool(project); const data = fs.readFileSync(projectPath, 'utf-8'); const fileList = JSON.parse(data); const compiledFiles = fileList.toString().replace(',', ' ').replace(/\.(c|cpp)\b/g, '.ll'); const totalFilePath = path.join(projectDir, 'total.ll'); + await waitForFiles(fileList.map((file) => file.replace(/\.(c|cpp)\b/g, '.ll'))); + try { const cliString = "llvm-link-15 -S " + compiledFiles + " -o " + totalFilePath child_process.execSync(cliString); diff --git a/src/project.ts b/src/project.ts index 76e89a7..864d0ec 100644 --- a/src/project.ts +++ b/src/project.ts @@ -179,38 +179,6 @@ export class ProjectEngine { */ start(doc: vscode.TextDocument, tool:ToolT): Thenable { return new Promise((resolve, reject) => { - // //не забыть поменять на правильный выбор директории - // let projectPath = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, 'tsar_project_file.json'); - - // if (!fs.existsSync(projectPath)) { - // await vscode.commands.executeCommand('tsar.refactorTsarProject') - // } - // const data = await fs.promises.readFile(projectPath, 'utf-8'); - // const fileList = JSON.parse(data); - // const fileListStrFormat = fileList.toString().replace(',', ' ') - // // let fileList = json.map(filepath => filepath.join(" ")); - - // let command = "tsar -emit-llvm -build-path=" + vscode.workspace.workspaceFolders[0].uri.fsPath + " " + fileListStrFormat; - - // // how send message, if class Project(who send messages) has constructor which need uri of a project, but tota.ll file is not created yet? - // // let message = new msg.CommandLine(command); - // // project.send(message); - - // child_process.execSync(command); - - // const compiledFiles = await fileListStrFormat.replace(/\.(c|cpp)\b/g, '.ll'); - // const totalFilePath = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, 'total.ll'); - - // try { - // child_process.execSync("llvm-link-15 -S " + compiledFiles + " -o " + totalFilePath); - // } catch (error) { - // const errorMessage = `Linking failed: ${error.stderr.toString()}`; - // vscode.window.showErrorMessage(errorMessage); - // } - - // let projectFileUri = vscode.Uri.file(totalFilePath); - // doc = await vscode.workspace.openTextDocument(projectFileUri); - let project = this.project(doc.uri); if (project !== undefined) { vscode.window.showWarningMessage( @@ -310,13 +278,13 @@ export class ProjectEngine { } // project should be *.json file - async runProjectTool(project: Project, query?: string) { + runProjectTool(project: Project, query?: string) { let cl = new msg.CommandLine(log.Extension.displayName); this._projects.forEach((val, key) =>{ cl.Args.push(vscode.Uri.file(key).fsPath); }) - cl.Args.push("-emit-llvm"); + // cl.Args.push("-emit-llvm"); cl.Query = "-emit-llvm"; // cl.Args.push(query); @@ -327,7 +295,7 @@ export class ProjectEngine { cl.Output = path.join(project.dirname, log.Project.output); cl.Error = path.join(project.dirname, log.Project.error); - await project.send(cl); + project.send(cl); return project; } @@ -743,34 +711,13 @@ export class Project { /** * Send request to a server. */ - send(request: any): Promise { - return new Promise((resolve, reject) => { + send(request: any) { let requestString = JSON.stringify(request) + log.Project.delimiter; log.Log.logs[0].write(log.Message.client.replace('{0}', requestString)); this._client.write(requestString) - - this._client.on('data', (data) => { - resolve(); - }); - this._client.on('error', (error) => { - reject(error); - }); - - this._client.on('end', () => { - reject(); - }); - - // this._client.on('data', (data) => { - // const response = data.toString(); - // console.log('Ответ от сервера:', response); - // }); - - // this._client.on('end', () => { - // console.log('Соединение с сервером закрыто.'); - // }); /*if (!this._client.write(requestString)) this._client.once('drain', () => {this.send(request)});*/ - })} + } /** * Register content provider with a specified base scheme.