diff --git a/CHANGELOG.md b/CHANGELOG.md
index 98d7354..568ac7a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,15 +2,23 @@
All notable changes to the "solidity-visual-auditor" extension will be documented in this file.
## v0.0.23
-- new: Update notifications! - can be disabled of course :)
+- new: Update notifications have arrived!
+- updated: solidity parser and surya
+- new: 🔥 Solidity Visual Auditor Cockpit panel
+ - Workspace Explorer
+ - Quick-access to extension settings
+ - Find Top Level Contracts
+ - Keep track of flattened files
+ - List public state-changing methods from the current contract
+ - Show the function call trace for the current method
## v0.0.22
-- update: solidity parser, surya (#41 #42)
+- update: solidity parser, surya (#41 [#42](https://github.com/tintinweb/vscode-solidity-auditor/issues/42))
- fix: linter warnings (#40)
- fix: configuration changes now take effect immediately (#43)
## v0.0.21
-- fix: Support VSCode for Windows (#38, #35)
+- fix: Support VSCode for Windows (#38, [#35](https://github.com/tintinweb/vscode-solidity-auditor/issues/35))
- fix: UML arrows (#34)
- code cleanup (#39)
- allow extension to run on unsaved files/editors (some functionality will not work on unsaved files, e.g. `surya` calls)
@@ -40,7 +48,7 @@ All notable changes to the "solidity-visual-auditor" extension will be documente
- new: codelense next to functions to generate sighash.
-- fix: function signature hashes are now generated for all functions (even internal ones, just ignore them for now :)). Canonicalization of types before calculating hashes #27.
+- fix: function signature hashes are now generated for all functions (even internal ones, just ignore them for now :)). Canonicalization of types before calculating hashes [#27](https://github.com/tintinweb/vscode-solidity-auditor/issues/27).
- new: alert on function sighash collision within the same contract.
diff --git a/README.md b/README.md
index f1673ce..c18a0f9 100644
--- a/README.md
+++ b/README.md
@@ -285,7 +285,15 @@ This feature is provided by [Inline Bookmarks](https://marketplace.visualstudio.
# Release Notes
## v0.0.23
-- new: Update notifications! - can be disabled of course :)
+- new: Update notifications have arrived!
+- updated: solidity parser and surya
+- new: 🔥 Solidity Visual Auditor Cockpit panel
+ - Workspace Explorer
+ - Quick-access to extension settings
+ - Find Top Level Contracts
+ - Keep track of flattened files
+ - List public state-changing methods from the current contract
+ - Show the function call trace for the current method
[Changelog](https://github.com/tintinweb/vscode-solidity-auditor/blob/master/CHANGELOG.md)
diff --git a/images/cmd-flatten.svg b/images/cmd-flatten.svg
new file mode 100644
index 0000000..46a24ab
--- /dev/null
+++ b/images/cmd-flatten.svg
@@ -0,0 +1,116 @@
+
+
diff --git a/images/glass3.png b/images/glass3.png
new file mode 100644
index 0000000..8df5097
Binary files /dev/null and b/images/glass3.png differ
diff --git a/images/panel-icon.svg b/images/panel-icon.svg
new file mode 100644
index 0000000..025abb2
--- /dev/null
+++ b/images/panel-icon.svg
@@ -0,0 +1,3793 @@
+
+
+
+
diff --git a/images/refresh-dark.svg b/images/refresh-dark.svg
new file mode 100644
index 0000000..2e184ea
--- /dev/null
+++ b/images/refresh-dark.svg
@@ -0,0 +1 @@
+
diff --git a/images/refresh-light.svg b/images/refresh-light.svg
new file mode 100644
index 0000000..a88fab8
--- /dev/null
+++ b/images/refresh-light.svg
@@ -0,0 +1 @@
+
diff --git a/package.json b/package.json
index ce20365..266b201 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"compiler",
"security"
],
- "version": "0.0.22",
+ "version": "0.0.23",
"publisher": "tintinweb",
"icon": "images/icon.png",
"engines": {
@@ -27,7 +27,26 @@
},
"activationEvents": [
"onLanguage:solidity",
- "onCommand:solidity-va.whatsNew.show"
+ "onCommand:solidity-va.whatsNew.show",
+ "onCommand:solidity-va.surya.graph",
+ "onCommand:solidity-va.surya.inheritance",
+ "onCommand:solidity-va.insights.topLevelContracts",
+ "onCommand:solidity-va.tools.flaterra",
+ "onCommand:solidity-va.cockpit.explorer.context.flatten",
+ "onCommand:solidity-va.tools.flattenCandidates",
+ "onCommand:solidity-va.tools.function.signatures",
+ "onCommand:solidity-va.tools.function.signatures.json",
+ "onCommand:solidity-va.tools.function.signatures.forWorkspace.json",
+ "onCommand:solidity-va.tools.remix.openExternal",
+ "onCommand:solidity-va.cockpit.explorer.refresh",
+ "onCommand:solidity-va.cockpit.topLevelContracts.refresh",
+ "onCommand:solidity-va.cockpit.settings.toggle",
+ "onView:solidity-va-cockpit-explorer",
+ "onView:solidity-va-cockpit-topLevelContracts",
+ "onView:solidity-va-cockpit-settings",
+ "onView:solidity-va-cockpit-ftrace",
+ "onView:solidity-va-cockpit-publicMethods",
+ "onView:solidity-va-cockpit"
],
"main": "./src/extension",
"capabilities": {
@@ -74,6 +93,96 @@
"path": "./src/themes/light_vs.json"
}
],
+ "viewsContainers": {
+ "activitybar": [
+ {
+ "id": "solidity-va-cockpit",
+ "title": "Solidity Visual Auditor",
+ "icon": "images/glass3.png"
+ }
+ ]
+ },
+ "views": {
+ "solidity-va-cockpit": [
+ {
+ "id": "solidity-va-cockpit-explorer",
+ "name": "Workspace: Explorer"
+ },
+ {
+ "id": "solidity-va-cockpit-topLevelContracts",
+ "name": "Workspace: Top Level Contracts"
+ },
+ {
+ "id": "solidity-va-cockpit-flatFiles",
+ "name": "Workspace: */flat_* Files",
+ "when": "ONLY_BETA"
+ },
+ {
+ "id": "solidity-va-cockpit-publicMethods",
+ "name": "Context: Public StateChanging Methods"
+ },
+ {
+ "id": "solidity-va-cockpit-ftrace",
+ "name": "Context: Function Call Trace",
+ "when": "ONLY_BETA"
+ },
+ {
+ "id": "solidity-va-cockpit-settings",
+ "name": "Settings"
+ }
+ ]
+ },
+ "menus": {
+ "view/title": [
+ {
+ "command": "solidity-va.cockpit.explorer.refresh",
+ "when": "view == solidity-va-cockpit-explorer",
+ "group": "navigation"
+ },
+ {
+ "command": "solidity-va.cockpit.topLevelContracts.refresh",
+ "when": "view == solidity-va-cockpit-topLevelContracts",
+ "group": "navigation"
+ },
+ {
+ "command": "solidity-va.cockpit.topLevelContracts.flatten",
+ "when": "view == solidity-va-cockpit-topLevelContracts",
+ "group": "navigation"
+ },
+ {
+ "command": "solidity-va.cockpit.flatFiles.refresh",
+ "when": "view == solidity-va-cockpit-flatFiles",
+ "group": "navigation"
+ }
+ ],
+ "view/item/context": [
+ {
+ "group": "solidity",
+ "command": "solidity-va.cockpit.topLevelContracts.refresh",
+ "when": "view == solidity-va-cockpit-explorer && viewItem =~ /^((?!\\.sol).)*$/"
+ },
+ {
+ "group": "solidity",
+ "command": "solidity-va.cockpit.explorer.context.flatten",
+ "when": "view == solidity-va-cockpit-explorer && viewItem =~ /\\.sol/ || view == solidity-va-cockpit-topLevelContracts"
+ },
+ {
+ "group": "solidity",
+ "command": "solidity-va.surya.mdreport",
+ "when": "view == solidity-va-cockpit-explorer || view == solidity-va-cockpit-topLevelContracts || view == solidity-va-cockpit-flatFiles"
+ },
+ {
+ "group": "solidity",
+ "command": "solidity-va.surya.graph",
+ "when": "view == solidity-va-cockpit-explorer || view == solidity-va-cockpit-topLevelContracts || view == solidity-va-cockpit-flatFiles"
+ },
+ {
+ "group": "solidity",
+ "command": "solidity-va.surya.inheritance",
+ "when": "view == solidity-va-cockpit-explorer || view == solidity-va-cockpit-topLevelContracts || view == solidity-va-cockpit-flatFiles"
+ }
+ ]
+ },
"commands": [
{
"command": "solidity-va.test.createTemplate",
@@ -110,6 +219,20 @@
"title": "Tools - flatten current file",
"category": "Solidity Visual Auditor"
},
+ {
+ "command": "solidity-va.cockpit.explorer.context.flatten",
+ "title": "Solidity - Flatten Selected File(s)",
+ "category": "Solidity Visual Auditor"
+ },
+ {
+ "command": "solidity-va.cockpit.topLevelContracts.flatten",
+ "title": "Flatten",
+ "category": "Solidity Visual Auditor",
+ "icon": {
+ "light": "images/cmd-flatten.svg",
+ "dark": "images/cmd-flatten.svg"
+ }
+ },
{
"command": "solidity-va.tools.flattenCandidates",
"title": "Tools - flatten all suggested top level contracts",
@@ -135,6 +258,38 @@
"title": "Tools - launch Remix-IDE",
"category": "Solidity Visual Auditor"
},
+ {
+ "command": "solidity-va.cockpit.explorer.refresh",
+ "title": "Scan Workspace",
+ "category": "Solidity Visual Auditor",
+ "icon": {
+ "light": "images/refresh-light.svg",
+ "dark": "images/refresh-dark.svg"
+ }
+ },
+ {
+ "command": "solidity-va.cockpit.topLevelContracts.refresh",
+ "title": "Find Top Level Contracts",
+ "category": "Solidity Visual Auditor",
+ "icon": {
+ "light": "images/refresh-light.svg",
+ "dark": "images/refresh-dark.svg"
+ }
+ },
+ {
+ "command": "solidity-va.cockpit.flatFiles.refresh",
+ "title": "Scan Workspace",
+ "category": "Solidity Visual Auditor",
+ "icon": {
+ "light": "images/refresh-light.svg",
+ "dark": "images/refresh-dark.svg"
+ }
+ },
+ {
+ "command": "solidity-va.cockpit.settings.toggle",
+ "title": "Toggle Setting",
+ "category": "Solidity Visual Auditor"
+ },
{
"command": "solidity-va.whatsNew.show",
"title": "Extension - What's New",
@@ -249,6 +404,15 @@
"default": false,
"description": "Enable/Disable actors in uml"
},
+ "solidity-va.cockpit.view.topLevelContracts.listStyle": {
+ "type": "string",
+ "enum": [
+ "flat",
+ "tree"
+ ],
+ "default": "flat",
+ "description": "Select TopLevelContracts view list style."
+ },
"solidity-va.whatsNew.disabled": {
"type": "boolean",
"default": false,
diff --git a/src/extension.js b/src/extension.js
index 0202cd5..c0a6776 100644
--- a/src/extension.js
+++ b/src/extension.js
@@ -8,6 +8,7 @@
/** imports */
const vscode = require('vscode');
const {CancellationTokenSource} = require('vscode');
+const path = require('path');
//const mod_codelens = require('./features/codelens');
const mod_hover = require('./features/hover');
@@ -19,6 +20,7 @@ const {DiliDiagnosticCollection} = require('./features/genericDiag');
const {Commands} = require('./features/commands');
const {SolidityCodeLensProvider} = require('./features/codelens');
const settings = require('./settings');
+const {Cockpit} = require('./features/cockpit.js');
const {WhatsNewHandler} = require('./features/whatsnew/whatsNew');
@@ -47,6 +49,17 @@ const ScopeEnum = {
/** helper */
+function editorJumptoRange(editor, range) {
+ let revealType = vscode.TextEditorRevealType.InCenter;
+ let selection = new vscode.Selection(range.start.line, range.start.character, range.end.line, range.end.character);
+ if (range.start.line === editor.selection.active.line) {
+ revealType = vscode.TextEditorRevealType.InCenterIfOutsideViewport;
+ }
+
+ editor.selection = selection;
+ editor.revealRange(selection, revealType);
+}
+
async function setDecorations(editor, decorations){
if (!editor) {
return;
@@ -611,6 +624,7 @@ function onActivate(context) {
onDidChange();
let commands = new Commands(g_parser);
+ let cockpit = new Cockpit(commands);
/** command setup */
context.subscriptions.push(
@@ -633,7 +647,8 @@ function onActivate(context) {
context.subscriptions.push(
vscode.commands.registerCommand(
'solidity-va.surya.mdreport',
- function (doc) {
+ function (doc, multiSelectTreeItems) {
+ doc = multiSelectTreeItems || doc;
commands.surya(doc || vscode.window.activeTextEditor.document, "mdreport");
}
)
@@ -643,6 +658,11 @@ function onActivate(context) {
vscode.commands.registerCommand(
'solidity-va.surya.graph',
function (doc, files) {
+ if(files && typeof files[0] === "object" && files[0].hasOwnProperty("children")){
+ //treeItem or fspaths
+ doc = files;
+ files = undefined;
+ }
commands.surya(doc || vscode.window.activeTextEditor.document, "graph", files);
}
)
@@ -650,7 +670,8 @@ function onActivate(context) {
context.subscriptions.push(
vscode.commands.registerCommand(
'solidity-va.surya.inheritance',
- function (doc) {
+ function (doc, multiSelectTreeItems) {
+ doc = multiSelectTreeItems || doc;
commands.surya(doc || vscode.window.activeTextEditor.document, "inheritance");
}
)
@@ -697,6 +718,27 @@ function onActivate(context) {
}
)
);
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'solidity-va.cockpit.explorer.context.flatten',
+ async function (treeItem, multiSelectTreeItems) {
+ multiSelectTreeItems = multiSelectTreeItems || [];
+ [...multiSelectTreeItems, treeItem].forEach(async treeItem => {
+ await vscode.extensions
+ .getExtension('tintinweb.vscode-solidity-flattener')
+ .activate()
+ .then(
+ async () => {
+ vscode.commands
+ .executeCommand('vscode-solidity-flattener.contextMenu.flatten', [], [treeItem.resource])
+ .then(async (done) => {});
+ });
+ });
+ }
+ )
+ );
+
context.subscriptions.push(
vscode.commands.registerCommand(
'solidity-va.tools.flattenCandidates',
@@ -706,6 +748,20 @@ function onActivate(context) {
)
);
+ context.subscriptions.push(
+ vscode.commands.registerCommand(
+ 'solidity-va.cockpit.topLevelContracts.flatten',
+ function () {
+ let sourceFiles = cockpit.views.topLevelContracts.dataProvider.data.reduce(function (obj, item) {
+ obj[path.basename(item.path,".sol")] = vscode.Uri.file(item.path);
+ return obj;
+ }, {});
+ commands.flattenCandidates(sourceFiles);
+ cockpit.views.flatFiles.refresh();
+ }
+ )
+ );
+
context.subscriptions.push(
vscode.commands.registerCommand(
'solidity-va.tools.function.signatures',
@@ -760,6 +816,49 @@ function onActivate(context) {
)
);
+ context.subscriptions.push(
+ vscode.commands.registerCommand("solidity-va.cockpit.topLevelContracts.refresh", async (treeItem, multiSelectTreeItems) => {
+ if(multiSelectTreeItems){
+ cockpit.views.topLevelContracts.refresh(multiSelectTreeItems.filter(t => !t.path.endsWith(".sol")).map(t => t.path));
+ } else {
+ cockpit.views.topLevelContracts.refresh(treeItem && treeItem.path);
+ }
+ })
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand("solidity-va.cockpit.explorer.refresh", async () => {
+ cockpit.views.explorer.refresh();
+ })
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand("solidity-va.cockpit.flatFiles.refresh", async () => {
+ cockpit.views.flatFiles.refresh();
+ })
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand("solidity-va.cockpit.jumpToRange", (documentUri, range) => {
+ vscode.workspace.openTextDocument(documentUri).then(doc => {
+ vscode.window.showTextDocument(doc).then(editor => {
+ if(range) {
+ editorJumptoRange(editor, range);
+ }
+ });
+ });
+ })
+ );
+
+ context.subscriptions.push(
+ vscode.commands.registerCommand("solidity-va.cockpit.settings.toggle", async (treeItem) => {
+ let cfg = vscode.workspace.getConfiguration(treeItem.metadata.extension);
+ let current = cfg.get(treeItem.metadata.section);
+ await cfg.update(treeItem.metadata.section, !current);
+ cockpit.views.settings.refresh();
+ })
+ );
+
/** event setup */
/***** DidChange */
vscode.window.onDidChangeActiveTextEditor(editor => {
@@ -784,6 +883,11 @@ function onActivate(context) {
onDidSave(document);
}, null, context.subscriptions);
+ /****** onDidChangeTextEditorSelection */
+ vscode.window.onDidChangeTextEditorSelection(event /* TextEditorVisibleRangesChangeEvent */ => {
+ cockpit.onDidSelectionChange(event); // let cockpit handle the event
+ }, null, context.subscriptions);
+
context.subscriptions.push(
vscode.languages.registerHoverProvider(type, {
diff --git a/src/features/cockpit.js b/src/features/cockpit.js
new file mode 100644
index 0000000..f8c988e
--- /dev/null
+++ b/src/features/cockpit.js
@@ -0,0 +1,826 @@
+'use strict';
+/**
+ * @author github.com/tintinweb
+ * @license MIT
+ *
+ *
+ * */
+const vscode = require('vscode');
+const settings = require('../settings.js');
+const surya = require('surya');
+const path = require('path');
+const fs = require('fs');
+
+/** views */
+
+class BaseView {
+ async refresh(value){
+ this.treeView.message = undefined; // clear the treeview message
+ return this.dataProvider.refresh(value);
+ }
+ async onDidSelectionChange(event) {}
+}
+
+class BaseDataProvider {
+ async dataGetRoot(){
+ return [];
+ }
+
+ dataGetChildren(element){
+ return null;
+ }
+
+ /** tree methods */
+ getChildren(element){
+ return element ? this.dataGetChildren(element): this.dataGetRoot();
+ }
+
+ getParent(element){
+ return element.parent;
+ }
+
+ getTreeItem(element){
+ return {
+ resourceUri: element.resource,
+ label: element.label,
+ iconPath: element.iconPath,
+ collapsibleState: element.collapsibleState,
+ children: element.children,
+ command: element.command || {
+ command: 'solidity-va.cockpit.jumpToRange',
+ arguments: [element.resource],
+ title: 'JumpTo'
+ }
+ };
+ }
+
+ /** other methods */
+ refresh(){
+ return new Promise((resolve, reject) => {
+ this._onDidChangeTreeData.fire();
+ resolve();
+ });
+ }
+}
+
+/** Generic Data Provider */
+/* helper */
+
+class FilePathTreeDataProvider extends BaseDataProvider {
+ constructor(listStyle, separator) {
+ super();
+ this.listStyle = listStyle;
+ this._separator = separator || path.sep;
+ this.data = [];
+ }
+
+ async dataGetRoot(){
+ return this.data;
+ }
+
+ dataGetChildren(element) {
+ if(!element) {
+ return this.data;
+ }
+ // element provided? -
+ return element.children;
+ }
+
+ dataGetParent(element){
+ return element.parent;
+ }
+
+ _addPathTree(uri){
+ //strip workspace path
+ let workspacePath = vscode.workspace.getWorkspaceFolder(uri).uri.fsPath;
+ let pathSegments = path.relative(workspacePath, uri.fsPath).split(this._separator);
+ let parent = this.data;
+
+ for(let idx = 0; idx < pathSegments.length; idx++){
+ let name = pathSegments[idx];
+ if(name == ""){
+ continue;
+ }
+ let pathObj = parent.find( p => p.name == name);
+
+ if(!pathObj){
+ //create a new one
+ let _path = pathSegments.slice(0, idx+1).join(this._separator);
+ let _abspath = path.join(workspacePath,_path);
+ let _type = FilePathTreeDataProvider.TYPE_FILE;
+ try {
+ _type = fs.lstatSync(_abspath).isDirectory() ? FilePathTreeDataProvider.TYPE_DIRECTORY : FilePathTreeDataProvider.TYPE_FILE;
+ } catch (err) {
+ console.warn(err); //fallback to type file
+ }
+
+ pathObj = {
+ name: name,
+ path: _path,
+ resource: vscode.Uri.file(_abspath),
+ children: [],
+ parent: parent,
+ type: _type,
+ workspace: workspacePath,
+ collapsibleState: _type === FilePathTreeDataProvider.TYPE_DIRECTORY ? vscode.TreeItemCollapsibleState.Collapsed : 0,
+ };
+ parent.push(pathObj);
+ }
+ parent = pathObj.children;
+ }
+ }
+
+ _addPathFlat(uri){
+ let pathSegments = uri.fsPath.split(this._separator);
+ let workspacePath = vscode.workspace.getWorkspaceFolder(uri).uri.fsPath;
+ this.data.push(
+ {
+ name: pathSegments[pathSegments.length - 1],
+ path: uri.fsPath,
+ resource: uri,
+ children: [],
+ parent: null,
+ type: FilePathTreeDataProvider.TYPE_FILE,
+ workspace: workspacePath,
+ collapsibleState: 0,
+ }
+ );
+ }
+
+ addPath(uri){
+ if(uri.scheme === undefined){
+ uri = vscode.Uri.file(uri);
+ }
+ if(this.listStyle === "flat"){
+ this._addPathFlat(uri);
+ } else {
+ this._addPathTree(uri);
+ }
+ }
+
+ load(paths) {
+ this.data = [];
+ for(let p of paths){
+ this.addPath(p);
+ }
+ }
+}
+FilePathTreeDataProvider.TYPE_DIRECTORY = 1;
+FilePathTreeDataProvider.TYPE_FILE = 2;
+
+class VirtualPathTreeDataProvider extends FilePathTreeDataProvider {
+
+ _addPathTree(s, metadata){
+ //strip workspace path
+ let pathSegments = s.split(this._separator);
+ let parent = this.data;
+
+ for(let idx = 0; idx < pathSegments.length; idx++){
+ let name = pathSegments[idx];
+ if(name == ""){
+ continue;
+ }
+ var pathObj = parent.find( p => p.name == name);
+
+ if(!pathObj){
+ //create a new one
+ let _path = pathSegments.slice(0, idx+1).join(this._separator);
+ let _type = idx == pathSegments.length -1 ? VirtualPathTreeDataProvider.TYPE_LEAF : VirtualPathTreeDataProvider.TYPE_NODE;
+ pathObj = {
+ name: name,
+ path: _path,
+ label: name,
+ metadata: metadata,
+ resource: null,
+ children: [],
+ parent: parent,
+ type: _type,
+ collapsibleState: _type == VirtualPathTreeDataProvider.TYPE_LEAF ? 0 : vscode.TreeItemCollapsibleState.Collapsed,
+ };
+ parent.push(pathObj);
+ }
+ parent = pathObj.children;
+ }
+ }
+
+ _addPathFlat(s, metadata){
+ let pathSegments = s.split(this._separator);
+ this.data.push(
+ {
+ name: pathSegments[pathSegments.length - 1],
+ path: s,
+ label: s,
+ metadata: metadata,
+ resource: null,
+ children: [],
+ parent: null,
+ type: VirtualPathTreeDataProvider.TYPE_LEAF,
+ collapsibleState: 0,
+ }
+ );
+ }
+
+ addPath(s, metadata){
+ if(this.listStyle === "flat"){
+ this._addPathFlat(s, metadata);
+ } else {
+ this._addPathTree(s, metadata);
+ }
+ }
+
+ load(paths) {
+ this.data = [];
+
+ if(Array.isArray(paths)){
+ for(let p of paths){
+ this.addPath(p);
+ }
+ } else {
+ for(let p of Object.keys(paths)){
+ this.addPath(p, paths[p]);
+ }
+ }
+
+ }
+}
+VirtualPathTreeDataProvider.TYPE_NODE = 1;
+VirtualPathTreeDataProvider.TYPE_LEAF = 2;
+
+/* TopLevelContracts View */
+
+class TopLevelContractsViewDataProvider extends FilePathTreeDataProvider {
+
+ constructor(treeView){
+ super(settings.extensionConfig().cockpit.view.topLevelContracts.listStyle);
+ this.treeView = treeView;
+ this._onDidChangeTreeData = new vscode.EventEmitter();
+ this.onDidChangeTreeData = this._onDidChangeTreeData.event;
+
+ this.data = null;
+ }
+
+ async dataGetRoot(){
+ return this.data || [];
+ }
+
+ /** events */
+
+ /** tree methods */
+ // inherited.
+
+ getTreeItem(element){
+ let ret = {
+ resourceUri: element.resource,
+ label: element.label,
+ iconPath: element.iconPath,
+ collapsibleState: element.collapsibleState,
+ command: element.type === FilePathTreeDataProvider.TYPE_FILE ? {
+ command: 'solidity-va.cockpit.jumpToRange',
+ arguments: [element.resource],
+ title: 'JumpTo'
+ } : 0,
+ };
+ return ret;
+ }
+
+ /** other methods */
+ refresh(workspaceRelativeBaseDir){
+ return new Promise((resolve, reject) => {
+ this.treeView.cockpit.commands._findTopLevelContracts(undefined, undefined, workspaceRelativeBaseDir).then(data => {
+ this.load(Object.values(data));
+ this._onDidChangeTreeData.fire();
+ resolve();
+ });
+ });
+ }
+}
+
+
+class DEPRECATED__TopLevelContractsViewDataProviderx extends BaseDataProvider {
+
+ constructor(treeView){
+ super();
+ this.treeView = treeView;
+ this._onDidChangeTreeData = new vscode.EventEmitter();
+ this.onDidChangeTreeData = this._onDidChangeTreeData.event;
+
+ this.data = null;
+ }
+
+ async dataGetRoot(){
+ if(this.data === null){
+ return [];
+ await this.refresh(); //first time: get data
+ }
+ return Object.keys(this.data).map(k => {
+ return {
+ resource: this.data[k], //uri
+ tooltip: k,
+ name: k,
+ parent: null,
+ iconPath: vscode.ThemeIcon.File,
+ };
+ });
+ }
+
+ dataGetChildren(){
+ return null; //no children :)
+ }
+
+ /** events */
+
+ /** tree methods */
+ // inherited.
+
+ /** other methods */
+ refresh(){
+ return new Promise((resolve, reject) => {
+ this.treeView.cockpit.commands._findTopLevelContracts().then(data => {
+ this.data = data;
+ this._onDidChangeTreeData.fire();
+ resolve();
+ });
+ });
+ }
+}
+
+
+class TopLevelContractsView extends BaseView {
+ constructor(cockpit) {
+ super();
+ this.cockpit = cockpit;
+ this.id = "topLevelContracts";
+ this.dataProvider = new TopLevelContractsViewDataProvider(this);
+ this.treeView = vscode.window.createTreeView(`solidity-va-cockpit-${this.id}`, { treeDataProvider:this.dataProvider });
+ this.treeView.message = "click ↻ to scan for contracts...";
+ }
+}
+
+/* FTrace View */
+
+class FTraceViewDataProvider extends BaseDataProvider {
+
+ constructor(treeView){
+ super();
+ this.treeView = treeView;
+ this._onDidChangeTreeData = new vscode.EventEmitter();
+ this.onDidChangeTreeData = this._onDidChangeTreeData.event;
+
+ this.data = null;
+ this.documentUri = null;
+ }
+
+ async dataGetRoot(){
+ if(this.data === null || this.documentUri === null){
+ return [];
+ }
+ return this.data.map(k => {
+ return {
+ resource: this.documentUri, //uri
+ label: k,
+ tooltip: k,
+ name: k,
+ parent: null,
+ iconPath: vscode.ThemeIcon.File,
+ };
+ });
+ }
+
+ dataGetChildren(){
+ return null; //no children :)
+ }
+
+ /** events */
+
+ /** tree methods */
+ // inherited.
+
+}
+
+class FTraceView extends BaseView {
+ constructor(cockpit) {
+ super();
+ this.cockpit = cockpit;
+ this.id = "ftrace";
+ this.dataProvider = new FTraceViewDataProvider(this);
+ this.treeView = vscode.window.createTreeView(`solidity-va-cockpit-${this.id}`, { treeDataProvider:this.dataProvider, showCollapseAll:true });
+ this.treeView.message = "click into the editor to update view...";
+ }
+
+ async onDidSelectionChange(event){
+
+ let documentUri = event.textEditor._documentData._uri;
+ let focus = event.selections[0].anchor;
+ let commands = this.cockpit.commands;
+
+ let contractObj = commands.g_parser.sourceUnits[documentUri.fsPath];
+ let knownFiles = Object.keys(commands.g_parser.sourceUnits).filter(f => f.endsWith(".sol"));
+
+
+ if(!contractObj){
+ console.warn("surya.ftrace: not a file: " + documentUri.fsPath);
+ return;
+ }
+
+ let focusSolidityElement = contractObj.getFunctionAtLocation(focus.line, focus.character);
+ if(!focusSolidityElement){
+ console.warn("surya.ftrace: contract not found: " + documentUri.fsPath);
+ return;
+ }
+ let contractName = focusSolidityElement.contract._node.name;
+
+ if(!focusSolidityElement.function){
+ return;
+ }
+
+ let functionName = focusSolidityElement.function._node.name;
+
+
+
+ let files;
+ if(settings.extensionConfig().tools.surya.input.contracts=="workspace"){
+ await vscode.workspace.findFiles("**/*.sol", settings.DEFAULT_FINDFILES_EXCLUDES, 500)
+ .then(uris => {
+ files = uris.map(function (uri) {
+ return uri.fsPath;
+ });
+ });
+ } else {
+ files = [documentUri.fsPath, ...knownFiles]; //better only add imported files. need to resolve that somehow
+ }
+
+ // contract::func, all, files
+ if (functionName === null){
+ functionName = "";
+ } else if (functionName === ""){
+ functionName = "";
+ }
+
+ let ret = surya.ftrace(contractName + "::" + functionName, 'all', files, {}, true).trim();
+ let lines = ret.match(/^.*((\r\n|\n|\r)|$)/gm);
+ this.dataProvider.documentUri = documentUri;
+ this.dataProvider.data = lines;
+ this.refresh();
+ }
+}
+
+/* Methods View */
+
+
+class PublicMethodsViewDataProvider extends FTraceViewDataProvider {
+
+ async dataGetRoot(){
+ if(this.data === null || this.documentUri === null){
+ return [];
+ }
+
+ return Object.keys(this.data)
+ .reduce((ret, key) => {
+ let element = this.data[key];
+ let range = new vscode.Range(element._node.loc.start.line, element._node.loc.start.column, element._node.loc.end.line, element._node.loc.end.column);
+ let modifiers = Object.keys(element.modifiers);
+ let item = {
+ resource: element.resource,
+ contextValue: element.resource.fsPath,
+ range: range,
+ label: element._node.stateMutability == "payable" ? key + " 💰 " : key,
+ tooltip: key,
+ name: key,
+ iconPath: vscode.ThemeIcon.File,
+ collapsibleState: modifiers.length > 0 ? vscode.TreeItemCollapsibleState.Collapsed : 0,
+ parent: null,
+ children: modifiers.map(name => {
+ return {
+ //resource: element.resource,
+ label: "Ⓜ " + name,
+ //iconPath: 0,
+ command: {
+ command: 'solidity-va.cockpit.jumpToRange',
+ arguments: [element.resource, range],
+ title: 'JumpTo'
+ }
+ };
+ }),
+ command: {
+ command: 'solidity-va.cockpit.jumpToRange',
+ arguments: [element.resource, range],
+ title: 'JumpTo'
+ },
+ };
+ ret.push(item);
+ return ret;
+ }, []);
+ }
+
+ dataGetChildren(element){
+ return element.children;
+ }
+
+ /** events */
+
+ /** tree methods */
+ // inherited.
+
+}
+
+class PublicMethodsView extends BaseView {
+ constructor(cockpit) {
+ super();
+ this.cockpit = cockpit;
+ this.id = "publicMethods";
+ this.dataProvider = new PublicMethodsViewDataProvider(this);
+ this.treeView = vscode.window.createTreeView(`solidity-va-cockpit-${this.id}`, { treeDataProvider:this.dataProvider });
+ this.treeView.message = "click into the editor to update view...";
+ }
+
+ async onDidSelectionChange(event){
+
+ let documentUri = event.textEditor._documentData._uri;
+ let focus = event.selections[0].anchor;
+ let commands = this.cockpit.commands;
+
+ let contractObj = commands.g_parser.sourceUnits[documentUri.fsPath];
+
+
+ if(!contractObj){
+ console.warn("cockpit.methods: not a file: " + documentUri.fsPath);
+ return;
+ }
+
+ let focusSolidityElement = contractObj.getFunctionAtLocation(focus.line, focus.character);
+ if(!focusSolidityElement){
+ console.warn("cockpit.methods: contract not found: " + documentUri.fsPath);
+ return;
+ }
+
+ let filterNotVisibility = ["private","internal"];
+ let filterNotStateMutability = ["view", "pure", "constant"];
+
+ let publicFunctions = Object.keys(focusSolidityElement.contract.functions)
+ .filter(f => {
+ let node = focusSolidityElement.contract.functions[f]._node;
+ //filter only for state changing public functions
+ return !filterNotVisibility.includes(node.visibility) && !filterNotStateMutability.includes(node.stateMutability);
+ })
+ .reduce((obj, key) => {
+ let newKey = key;
+ let func = focusSolidityElement.contract.functions[key];
+
+ if (key === null || func._node.isConstructor){
+ newKey = "";
+ } else if (key === "" || func._node.isFallback){
+ newKey = "";
+ }
+ func.resource = documentUri;
+ obj[newKey] = func;
+ return obj;
+ }, {});
+ // contract::func, all, files
+ this.dataProvider.documentUri = documentUri;
+ this.dataProvider.data = publicFunctions;
+ this.refresh();
+ }
+}
+
+
+/* Solidity Files View */
+
+class ExplorerViewDataProvider extends FilePathTreeDataProvider {
+ constructor(treeView){
+ super("tree");
+ this.treeView = treeView;
+ this._onDidChangeTreeData = new vscode.EventEmitter();
+ this.onDidChangeTreeData = this._onDidChangeTreeData.event;
+
+ this.data = null;
+ }
+
+ async dataGetRoot(){
+ if(this.data === null){
+ this.refresh();
+ }
+ return this.data || [];
+ }
+
+ /** events */
+
+ /** tree methods */
+ // inherited.
+
+ getTreeItem(element){
+ let ret = {
+ resourceUri: element.resource,
+ contextValue: element.resource.fsPath,
+ label: element.label,
+ iconPath: element.iconPath,
+ collapsibleState: element.collapsibleState,
+ command: element.type === FilePathTreeDataProvider.TYPE_FILE ? {
+ command: 'solidity-va.cockpit.jumpToRange',
+ arguments: [element.resource],
+ title: 'JumpTo'
+ } : 0,
+ };
+ return ret;
+ }
+
+ /** other methods */
+ refresh(){
+ return new Promise((resolve, reject) => {
+ vscode.workspace.findFiles("{**/*.sol}", settings.DEFAULT_FINDFILES_EXCLUDES_ALLOWFLAT, 5000)
+ .then((solfiles) => {
+ this.load(solfiles);
+ this._onDidChangeTreeData.fire();
+ resolve();
+ });
+ });
+ }
+}
+
+class ExplorerView extends BaseView {
+ constructor(cockpit) {
+ super();
+ this.cockpit = cockpit;
+ this.id = "explorer";
+ this.dataProvider = new ExplorerViewDataProvider(this);
+ this.treeView = vscode.window.createTreeView(`solidity-va-cockpit-${this.id}`, { treeDataProvider:this.dataProvider, showCollapseAll:true, canSelectMany:true });
+ }
+}
+
+class FlatFilesDataProvider extends FilePathTreeDataProvider {
+ constructor(treeView){
+ super("tree");
+ this.treeView = treeView;
+ this._onDidChangeTreeData = new vscode.EventEmitter();
+ this.onDidChangeTreeData = this._onDidChangeTreeData.event;
+
+ this.data = null;
+ }
+
+ async dataGetRoot(){
+ if(this.data === null){
+ this.refresh();
+ }
+ return this.data || [];
+ }
+
+ /** events */
+
+ /** tree methods */
+ // inherited.
+
+ getTreeItem(element){
+ let ret = {
+ resourceUri: element.resource,
+ contextValue: element.resource.fsPath,
+ label: element.label,
+ iconPath: element.iconPath,
+ collapsibleState: element.collapsibleState,
+ command: element.type === FilePathTreeDataProvider.TYPE_FILE ? {
+ command: 'solidity-va.cockpit.jumpToRange',
+ arguments: [element.resource],
+ title: 'JumpTo'
+ } : 0,
+ };
+ return ret;
+ }
+
+ /** other methods */
+ refresh(){
+ return new Promise((resolve, reject) => {
+ vscode.workspace.findFiles("{**/*_flat.sol,**/flat_*.sol}", settings.DEFAULT_FINDFILES_EXCLUDES_ALLOWFLAT, 500)
+ .then((solfiles) => {
+ this.load(solfiles);
+ this._onDidChangeTreeData.fire();
+ resolve();
+ });
+ });
+ }
+}
+
+class FlatFilesView extends BaseView {
+ constructor(cockpit) {
+ super();
+ this.cockpit = cockpit;
+ this.id = "flatFiles";
+ this.dataProvider = new FlatFilesDataProvider(this);
+ this.treeView = vscode.window.createTreeView(`solidity-va-cockpit-${this.id}`, { treeDataProvider:this.dataProvider, showCollapseAll:true, canSelectMany:true });
+ }
+}
+
+/* settings view */
+class SettingsViewDataProvider extends VirtualPathTreeDataProvider {
+ constructor(treeView){
+ super("tree", ".");
+ this.treeView = treeView;
+ this._onDidChangeTreeData = new vscode.EventEmitter();
+ this.onDidChangeTreeData = this._onDidChangeTreeData.event;
+
+ this.data = null;
+
+ let pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..","..",'package.json')));
+ let properties = pkg.contributes.configuration.properties;
+ this.settings = Object.keys(properties)
+ .filter(key => properties[key].type === "boolean")
+ .reduce((obj, key) => {
+ obj[key] = properties[key];
+ return obj;
+ }, {});
+ }
+
+ async dataGetRoot(){
+ if(this.data === null){
+ this.refresh();
+ }
+ return this.data || [];
+ }
+
+ /** events */
+
+ /** tree methods */
+ // inherited.
+
+ getTreeItem(element){
+ let ret = {
+ resourceUri: element.resource,
+ metadata: element.metadata,
+ contextValue: element.type,
+ label: element.type === VirtualPathTreeDataProvider.TYPE_LEAF ? (element.metadata.currentValue===true ? "☑ " : "☐ ") + element.label : element.label,
+ tooltip: element.type === VirtualPathTreeDataProvider.TYPE_LEAF ? element.metadata.description : null,
+ iconPath: element.iconPath,
+ collapsibleState: element.collapsibleState,
+ command: element.type === VirtualPathTreeDataProvider.TYPE_LEAF ? {
+ command: 'solidity-va.cockpit.settings.toggle',
+ arguments: [element],
+ title: 'Toggle'
+ } : 0,
+ };
+ return ret;
+ }
+
+ /** other methods */
+ refresh(){
+ return new Promise((resolve, reject) => {
+ let settingsState = Object.keys(this.settings)
+ .reduce((obj, key) => {
+ obj[key] = this.settings[key];
+ let k = key.split(".");
+ obj[key].extension = k[0];
+ obj[key].section = k.slice(1).join(".");
+ obj[key].currentValue = vscode.workspace.getConfiguration(obj[key].extension).get(obj[key].section);
+ return obj;
+ }, {});
+ this.load(settingsState);
+ this._onDidChangeTreeData.fire();
+ resolve();
+ });
+ }
+}
+
+class SettingsView extends BaseView {
+ constructor(cockpit) {
+ super();
+ this.cockpit = cockpit;
+ this.id = "settings";
+ this.dataProvider = new SettingsViewDataProvider(this);
+ this.treeView = vscode.window.createTreeView(`solidity-va-cockpit-${this.id}`, { treeDataProvider:this.dataProvider, showCollapseAll:true });
+ }
+}
+
+/** -- cockpit handler -- */
+class Cockpit {
+
+ constructor(commands) {
+ this.commands = commands;
+ this.views = {};
+
+ this.registerView(new ExplorerView(this));
+ this.registerView(new TopLevelContractsView(this));
+ this.registerView(new FlatFilesView(this));
+ this.registerView(new FTraceView(this));
+ this.registerView(new SettingsView(this));
+ this.registerView(new PublicMethodsView(this));
+ }
+
+ registerView(view) {
+ this.views[view.id] = view;
+ }
+
+ async onDidSelectionChange(event){
+
+ if(event.textEditor._visibleRanges.length <= 0 || event.selections.length <= 0){
+ return; // no visible range open; no selection
+ }
+
+ Object.keys(this.views).forEach(k => {
+ let v = this.views[k];
+ if(v.treeView.visible){
+ v.onDidSelectionChange(event);
+ }
+ });
+ }
+}
+
+
+module.exports = {
+ Cockpit: Cockpit
+};
\ No newline at end of file
diff --git a/src/features/commands.js b/src/features/commands.js
index a5a0824..6c580c2 100644
--- a/src/features/commands.js
+++ b/src/features/commands.js
@@ -116,24 +116,56 @@ class Commands{
.then(doc => vscode.window.showTextDocument(doc, vscode.ViewColumn.Beside));
}
- async surya(document, command, args) {
- // run surya and maybe return output in new window
- this._checkIsSolidity(document); // throws
-
+ async surya(documentOrListItems, command, args) {
+ //check if input was document or listItem
+ if(!documentOrListItems){
+ throw new Error("not a file or list item");
+ }
+
let ret;
+ let files = [];
- let files;
+ if(documentOrListItems.hasOwnProperty("children")){
+ //hack ;)
+ documentOrListItems = [documentOrListItems]; //allow non array calls
+ }
- if(settings.extensionConfig().tools.surya.input.contracts=="workspace"){
- await vscode.workspace.findFiles("**/*.sol",'**/node_modules', 500)
- .then(uris => {
- files = uris.map(function (uri) {
- return uri.fsPath;
- });
- });
+ if(Array.isArray(documentOrListItems)){
+
+ for(let documentOrListItem of documentOrListItems){
+
+ if(documentOrListItem.hasOwnProperty("children")){
+ // is a list item -> item.resource.fsPath
+ if(!!path.extname(documentOrListItem.resource.fsPath)){
+ //file
+ files = [...files, documentOrListItem.resource.fsPath];
+ } else {
+ //folder
+ await vscode.workspace.findFiles(`${documentOrListItem.path}/**/*.sol`, settings.DEFAULT_FINDFILES_EXCLUDES, 500)
+ .then(uris => {
+ files = files.concat(uris.map(function (uri) {
+ return uri.fsPath;
+ }));
+ });
+ }
+ }
+
+ }
} else {
- files = [document.uri.fsPath, ...Object.keys(this.g_parser.sourceUnits)]; //better only add imported files. need to resolve that somehow
- }
+ //single document mode
+ this._checkIsSolidity(documentOrListItems); // throws
+
+ if(settings.extensionConfig().tools.surya.input.contracts=="workspace"){
+ await vscode.workspace.findFiles("**/*.sol", settings.DEFAULT_FINDFILES_EXCLUDES, 500)
+ .then(uris => {
+ files = uris.map(function (uri) {
+ return uri.fsPath;
+ });
+ });
+ } else {
+ files = [documentOrListItems.uri.fsPath, ...Object.keys(this.g_parser.sourceUnits)]; //better only add imported files. need to resolve that somehow
+ }
+ }
switch(command) {
case "describe":
@@ -242,6 +274,9 @@ class Commands{
break;
case "mdreport":
ret = surya.mdreport(files);
+ if(!ret) {
+ return;
+ }
vscode.workspace.openTextDocument({content: ret, language: "markdown"})
.then(doc => {
if(settings.extensionConfig().preview.markdown){
@@ -267,18 +302,26 @@ class Commands{
}
}
- async _findTopLevelContracts(files, scanfiles) {
+ async _findTopLevelContracts(files, scanfiles, workspaceRelativeBaseDirs) {
var that = this;
var dependencies={};
var contractToFile={};
if(!scanfiles){
- await vscode.workspace.findFiles("**/*.sol",'{**/node_modules,**/mock*,**/test*,**/migrations,**/Migrations.sol,**/flat_*.sol}', 500)
+
+ workspaceRelativeBaseDirs = Array.isArray(workspaceRelativeBaseDirs) ? workspaceRelativeBaseDirs : [workspaceRelativeBaseDirs];
+
+ let searchFileString = "{" +workspaceRelativeBaseDirs.map(d => d === undefined ? "**/*.sol" : d + path.sep + "**/*.sol").join(",") + "}";
+
+ await vscode.workspace.findFiles(searchFileString, settings.DEFAULT_FINDFILES_EXCLUDES, 500)
.then((solfiles) => {
solfiles.forEach(function(solfile){
try {
- let content = fs.readFileSync(solfile.path).toString('utf-8');
+ let content = fs.readFileSync(solfile.fsPath).toString('utf-8');
let sourceUnit = that.g_parser.parseSourceUnit(content);
for(let contractName in sourceUnit.contracts){
+ if(sourceUnit.contracts[contractName]._node.kind == "interface") { //ignore interface contracts
+ continue;
+ }
dependencies[contractName] = sourceUnit.contracts[contractName].dependencies;
contractToFile[contractName] = solfile;
}
@@ -291,6 +334,9 @@ class Commands{
//files not set: take loaded sourceUnits from this.g_parser
//files set: only take these sourceUnits
for(let contractName in this.g_parser.contracts){
+ if(this.g_parser.contracts[contractName]._node.kind == "interface") {
+ continue;
+ }
dependencies[contractName] = this.g_parser.contracts[contractName].dependencies;
}
}
@@ -397,9 +443,11 @@ ${topLevelContractsText}`;
});
}
- async flattenCandidates() {
- let topLevelContracts = await this._findTopLevelContracts();
+ async flattenCandidates(candidates) {
+ // takes object key=contractName value=fsPath
+ let topLevelContracts = candidates || await this._findTopLevelContracts();
let content = "";
+ let trufflepath = undefined;
this.solidityFlattener(Object.values(topLevelContracts), (filepath, trufflepath, content) => {
@@ -415,8 +463,9 @@ ${topLevelContractsText}`;
for(let name in topLevelContracts){
//this.flaterra(new vscode.Uri(topLevelContracts[name]))
- let outpath = path.parse(topLevelContracts[name].path);
- content += name + " => " + vscode.Uri.file(path.join(outpath.dir, "flat_" + outpath.base)) + "\n";
+ let outpath = path.parse(topLevelContracts[name].fsPath);
+ let outpath_flat = vscode.Uri.file(path.join(outpath.dir, "flat_" + outpath.base));
+ content += `${!fs.existsSync(outpath_flat.fsPath)?"[ERR] ":""}${name} => ${outpath_flat} \n`;
}
vscode.workspace.openTextDocument({content: content, language: "markdown"})
.then(doc => vscode.window.showTextDocument(doc, vscode.ViewColumn.Beside));
@@ -453,7 +502,7 @@ ${topLevelContractsText}`;
let sighashes = {};
let collisions = [];
- await vscode.workspace.findFiles("**/*.sol",'**/node_modules', 500)
+ await vscode.workspace.findFiles("**/*.sol", settings.DEFAULT_FINDFILES_EXCLUDES, 500)
.then(uris => {
uris.forEach(uri => {
try {
diff --git a/src/features/genericDiag.js b/src/features/genericDiag.js
index a5833ff..20dc275 100644
--- a/src/features/genericDiag.js
+++ b/src/features/genericDiag.js
@@ -128,7 +128,8 @@ class DiliDiagnosticCollection {
});
} catch (err) {
- console.error(err);
+ console.warn(f);
+ console.warn(err);
}
});
});
diff --git a/src/features/parser.js b/src/features/parser.js
index dfbdf44..0691df4 100644
--- a/src/features/parser.js
+++ b/src/features/parser.js
@@ -52,6 +52,57 @@ const reservedKeywords = [
"typeof",
"unchecked"];
+class SourceUnit {
+ constructor(parser){
+ this.parser = parser;
+ this.contracts = {};
+ this.pragmas = [];
+ this.imports = [];
+ this.hash = undefined;
+ this.filepath = undefined;
+ this.commentMapper = undefined;
+ }
+
+ getContractAtLocation(line, column){
+ for(let c of Object.keys(this.contracts)){
+ let loc = this.contracts[c]._node.loc;
+ if(line < loc.start.line){
+ continue;
+ } else if (line == loc.start.line && column < loc.start.column) {
+ continue;
+ } else if (line == loc.end.line && column > loc.end.column) {
+ continue;
+ } else if (line > loc.end.line){
+ continue;
+ }
+
+ return this.contracts[c];
+ }
+ }
+
+ getFunctionAtLocation(line, column){
+ let contract = this.getContractAtLocation(line, column);
+ if(!contract){
+ return;
+ }
+ for(let c of Object.keys(contract.functions)){
+ let loc = contract.functions[c]._node.loc;
+ if(line < loc.start.line){
+ continue;
+ } else if (line == loc.start.line && column < loc.start.column) {
+ continue;
+ } else if (line == loc.end.line && column > loc.end.column) {
+ continue;
+ } else if (line > loc.end.line){
+ continue;
+ }
+
+ return {contract, function:contract.functions[c]};
+ }
+ return {contract};
+ }
+}
+
class SolidityParser{
constructor() {
@@ -103,7 +154,7 @@ class SolidityParser{
sourceUnit.imports.forEach(function(imp){
//basedir
- let fileWorkspace = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(filepath)).path;
+ let fileWorkspace = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(filepath)).uri.fsPath;
let relativeNodeModules = function(){
let basepath = filepath.split("/contracts/");
@@ -204,12 +255,7 @@ class SolidityParser{
console.error("solidity-parser-diligence - failed to parse input");
}
- var sourceUnit = {
- contracts:{},
- pragmas:[],
- imports:[],
- hash:null
- };
+ let sourceUnit = new SourceUnit(this);
/** AST rdy */
var current_contract=null;
diff --git a/src/features/whatsnew/whatsNew.js b/src/features/whatsnew/whatsNew.js
index ea5c7a0..b65e093 100644
--- a/src/features/whatsnew/whatsNew.js
+++ b/src/features/whatsnew/whatsNew.js
@@ -21,10 +21,43 @@ Hey there 🙌 **Solidity Visual Auditor** just got better! Find out more below.
### What's New?
+We've been working on a new cockpit view that allows you to navigate large codebases more efficiently. Check out the new
icon in the activity bar to your left.
+
+So, what can you do with it?
+
+- Explore .sol files with the new workspace explorer
+- Generate report/graphs for any files/folders selected in the explorer views
+- Conveniently flatten selected files (selected folders or all files in the top-level view) (Note: \`truffle-flattener\` may require an \`npm install\` of the project for flattening to work)
+- Search for contracts that are likely to be deployed in the system (complete workspace or selected folders)
+
+
+
+- Context-sensitive views: click into a contract in the editor to list public state-changing methods
+
+
+
+- Get quick access to extension settings
+
+
+
+And there is more to come 🙌 stay tuned!
+
+
+The cockpit view is fully customizable. You can hide both the sidebar menu or any view in the cockpit that you do not need (right-click → hide).
+
+
The complete changelog can be found [here](https://github.com/ConsenSys/vscode-solidity-auditor/blob/master/CHANGELOG.md).
#### v0.0.23
- new: Update notifications have arrived!
+- updated: solidity parser and surya
+- new: 🔥 Solidity Visual Auditor Cockpit panel
+ - Workspace Explorer
+ - Quick-access to extension settings
+ - Find Top Level Contracts
+ - Keep track of flattened files
+ - List public state-changing methods from the current contract
+ - Show the function call trace for the current method
Hint: disable future notification? \`settings → solidity-va.whatsNew.disabled : true\`
diff --git a/src/settings.js b/src/settings.js
index 0386c28..c236496 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -11,6 +11,9 @@ const docSelector = {
language: languageId
};
+const DEFAULT_FINDFILES_EXCLUDES = '{**/node_modules,**/mock*,**/test*,**/migrations,**/Migrations.sol,**/flat_*.sol}';
+const DEFAULT_FINDFILES_EXCLUDES_ALLOWFLAT = '{**/node_modules,**/mock*,**/test*,**/migrations,**/Migrations.sol}';
+
function extensionConfig() {
return vscode.workspace.getConfiguration('solidity-va');
}
@@ -23,5 +26,7 @@ module.exports = {
extensionConfig: extensionConfig,
languageId: languageId,
docSelector: docSelector,
- extension: extension
+ extension: extension,
+ DEFAULT_FINDFILES_EXCLUDES: DEFAULT_FINDFILES_EXCLUDES,
+ DEFAULT_FINDFILES_EXCLUDES_ALLOWFLAT: DEFAULT_FINDFILES_EXCLUDES_ALLOWFLAT
};
\ No newline at end of file
diff --git a/whats_new/icon.png b/whats_new/icon.png
new file mode 100644
index 0000000..a7b1a62
Binary files /dev/null and b/whats_new/icon.png differ
diff --git a/whats_new/index.html b/whats_new/index.html
index d0fdf28..0400149 100644
--- a/whats_new/index.html
+++ b/whats_new/index.html
@@ -29,9 +29,9 @@
line-height: 1.6;
padding-top: 10px;
padding-bottom: 10px;
- background-color: white;
+ x-background-color: white;
padding: 30px;
- color: #333;
+ x-color: #333;
}
body > *:first-child {
@@ -409,7 +409,6 @@
background-color: transparent;
border: none;
}
- body { background-color: white; color: black}
-
+
\ No newline at end of file