Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add open configuration commands to Command Palette (VSCode) #648

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/VisualStudioCode/package/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 13 additions & 3 deletions src/VisualStudioCode/package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"CSharp"
],
"engines": {
"vscode": "^1.31.0"
"vscode": "^1.42.0"
},
"files": [
"roslyn/*"
Expand All @@ -38,6 +38,16 @@
"*"
],
"contributes": {
"commands": [
{
"title": "Roslynator: Open Configuration of Analyzers (roslynator.ruleset)",
"command": "roslynator.openConfigurationOfAnalyzers"
},
{
"title": "Roslynator: Open Configuration of Refactorings and Fixes (roslynator.config)",
"command": "roslynator.openConfigurationOfRefactoringsAndFixes"
}
],
"configuration": {
"title": "Roslynator",
"properties": {
Expand All @@ -63,7 +73,7 @@
"@types/json5": "0.0.30",
"@types/mocha": "^5.2.7",
"@types/node": "^12.11.7",
"@types/vscode": "^1.31.0",
"@types/vscode": "^1.42.0",
"fs-extra": "^8.1.0",
"glob": "^7.1.5",
"mocha": "^6.2.2",
Expand All @@ -74,4 +84,4 @@
"dependencies": {
"json5": "^2.1.1"
}
}
}
27 changes: 27 additions & 0 deletions src/VisualStudioCode/package/src/configurationFiles.generated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export const configurationFileContent = {
ruleset: `<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="roslynator.ruleset" ToolsVersion="16.0">
<!-- Specify default action that should be applied to all analyzers except those explicitly specified. -->
<!-- <IncludeAll Action="None,Hidden,Info,Warning,Error" /> -->
<Rules AnalyzerId="Roslynator.CSharp.Analyzers" RuleNamespace="Roslynator.CSharp.Analyzers">
<!-- Specify default action that should be applied to a specified analyzer. -->
<!-- <Rule Id="RCSxxxx" Action="None,Hidden,Info,Warning,Error" /> -->
</Rules>
</RuleSet>`,
config: `<?xml version="1.0" encoding="utf-8"?>
<Roslynator>
<Settings>
<General>
<!-- <PrefixFieldIdentifierWithUnderscore>true</PrefixFieldIdentifierWithUnderscore> -->
</General>
<Refactorings>
<!-- <Refactoring Id="RRxxxx" IsEnabled="false" /> -->
</Refactorings>
<CodeFixes>
<!-- <CodeFix Id="CSxxxx.RCFxxxx" IsEnabled="false" /> -->
<!-- <CodeFix Id="CSxxxx" IsEnabled="false" /> -->
<!-- <CodeFix Id="RCFxxxx" IsEnabled="false" /> -->
</CodeFixes>
</Settings>
</Roslynator>`
};
95 changes: 21 additions & 74 deletions src/VisualStudioCode/package/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,94 +1,41 @@
import * as vscode from 'vscode';
import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs';
import * as json5 from 'json5';
import { OmnisharpSettingsKey } from './omnisharpSettings';
import { Context } from './context';
import { configurationFileContent } from './configurationFiles.generated';
import { ensureOmnisharpConfigurationUpdated } from './updateOmnisharpConfiguration';
import { openRoslynatorConfiguration } from './openRoslynatorConfiguration';

export function activate(context: vscode.ExtensionContext) {
const shouldAutoUpdate = vscode.workspace.getConfiguration()
.get('roslynator.autoUpdateOmnisharpJson');

if (shouldAutoUpdate) {
ensureConfigurationUpdated({
ensureOmnisharpConfigurationUpdated({
homeDirectoryPath: os.homedir(),
extensionDirectoryPath: context.extensionPath
});
}
}

export function deactivate() { }

export function ensureConfigurationUpdated(context: Context) {
const omnisharpDirectoryPath = path.join(context.homeDirectoryPath, '.omnisharp');

if (!fs.existsSync(omnisharpDirectoryPath)) {
fs.mkdirSync(omnisharpDirectoryPath);
}

const omnisharpJsonPath = path.join(omnisharpDirectoryPath, 'omnisharp.json');

let omnisharpSettings: any;
let settingsUpdated = false;

if (fs.existsSync(omnisharpJsonPath)) {
const omnisharpJson = fs.readFileSync(omnisharpJsonPath, { encoding: 'utf8' });
omnisharpSettings = json5.parse(omnisharpJson);
} else {
omnisharpSettings = {};
}

const roslynExtensionsOptionsKey = findKey(omnisharpSettings, OmnisharpSettingsKey.RoslynExtensionsOptions);

if (!omnisharpSettings[roslynExtensionsOptionsKey] || typeof omnisharpSettings[roslynExtensionsOptionsKey] !== 'object') {
omnisharpSettings[roslynExtensionsOptionsKey] = {};
}

const enableAnalyzersSupportKey = findKey(omnisharpSettings[roslynExtensionsOptionsKey], OmnisharpSettingsKey.EnableAnalyzersSupport);
const localAppDataPath = getLocalAppDataPath();

if (omnisharpSettings[roslynExtensionsOptionsKey][enableAnalyzersSupportKey] !== true) {
omnisharpSettings[roslynExtensionsOptionsKey][enableAnalyzersSupportKey] = true;
settingsUpdated = true;
}

const locationPathsKey = findKey(omnisharpSettings[roslynExtensionsOptionsKey], OmnisharpSettingsKey.LocationPaths);

if (!Array.isArray(omnisharpSettings[roslynExtensionsOptionsKey][locationPathsKey])) {
omnisharpSettings[roslynExtensionsOptionsKey][locationPathsKey] = [];
}

const roslynPath = path.join(context.extensionDirectoryPath, 'roslyn');
const locationPaths = [
path.join(roslynPath, 'common'),
path.join(roslynPath, 'analyzers'),
path.join(roslynPath, 'refactorings'),
path.join(roslynPath, 'fixes')
].map(p => p.replace(/\\/g, '/'));

const containsPaths = locationPaths.every(
p => (omnisharpSettings[roslynExtensionsOptionsKey][locationPathsKey] as any[]).includes(p));

if (!containsPaths) {
const unrelatedPaths: string[] = (omnisharpSettings[roslynExtensionsOptionsKey][locationPathsKey] as any[])
.filter(p => typeof p === 'string'
&& !p.includes('josefpihrt-vscode.roslynator') && !locationPaths.includes(p));
context.subscriptions.push(
vscode.commands.registerCommand(
'roslynator.openConfigurationOfAnalyzers',
() => openRoslynatorConfiguration('roslynator.ruleset', configurationFileContent.ruleset, localAppDataPath)),
vscode.commands.registerCommand(
'roslynator.openConfigurationOfRefactoringsAndFixes',
() => openRoslynatorConfiguration('roslynator.config', configurationFileContent.config, localAppDataPath))
);
}

omnisharpSettings[roslynExtensionsOptionsKey][locationPathsKey] = [
...unrelatedPaths,
...locationPaths
];
settingsUpdated = true;
}
export function deactivate() { }

if (settingsUpdated) {
const updatedOmnisharpJson = JSON.stringify(omnisharpSettings, null, 4);
fs.writeFileSync(omnisharpJsonPath, updatedOmnisharpJson);
function getLocalAppDataPath() {
const platform = os.platform();

vscode.window.showInformationMessage('omnisharp.json has been updated with Roslynator configuration.');
if (platform === 'win32') {
return path.join(os.homedir(), 'AppData', 'Local');
} else if (platform === 'linux' || platform === 'darwin') {
return path.join(os.homedir(), '.local', 'share');
}
}

function findKey(settings: any, key: OmnisharpSettingsKey) {
return Object.keys(settings).find(k => k.toLowerCase() === key.toLowerCase()) ?? key;
}
24 changes: 24 additions & 0 deletions src/VisualStudioCode/package/src/openRoslynatorConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';

export async function openRoslynatorConfiguration(fileName: string, content: string, localAppDataPath: string | undefined) {
if (!localAppDataPath) {
return;
}

const configurationDirectoryPath = path.join(localAppDataPath, 'JosefPihrt', 'Roslynator', 'VisualStudioCode');

if (!fs.existsSync(configurationDirectoryPath)) {
fs.mkdirSync(configurationDirectoryPath, { recursive: true });
}

const configurationFilePath = path.join(configurationDirectoryPath, fileName);

if (!fs.existsSync(configurationFilePath)) {
fs.writeFileSync(configurationFilePath, content);
}

const document = await vscode.workspace.openTextDocument(configurationFilePath);
await vscode.window.showTextDocument(document);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as assert from 'assert';

import * as vscode from 'vscode';
import * as fs from 'fs-extra';
import * as path from 'path';

import { openRoslynatorConfiguration } from '../../openRoslynatorConfiguration';

suite('Open Roslynator configuration file', () => {
test('Open configuration file for the first time (non-existing)', async () => {
const appDataPath = path.join(__dirname, 'tempOpenForTheFirstTimeAppData');

if (fs.existsSync(appDataPath)) {
fs.removeSync(appDataPath);
}

await openRoslynatorConfiguration('roslynator.ruleset', 'test content', appDataPath);

const openFilePath = vscode.window.activeTextEditor?.document.uri.path;
const openFileContent = vscode.window.activeTextEditor?.document.getText();

assert.ok(fs.existsSync(path.join(appDataPath, 'JosefPihrt', 'Roslynator', 'VisualStudioCode', 'roslynator.ruleset')));
assert.ok(openFilePath?.endsWith('tempOpenForTheFirstTimeAppData/JosefPihrt/Roslynator/VisualStudioCode/roslynator.ruleset'));
assert.equal(openFileContent, 'test content');
});

test('Open existing configuration file', async () => {
const appDataPath = path.join(__dirname, 'tempOpenExistingAppData');

if (fs.existsSync(appDataPath)) {
fs.removeSync(appDataPath);
}

const roslynatorConfigurationDirectoryPath = path.join(appDataPath, 'JosefPihrt', 'Roslynator', 'VisualStudioCode');

fs.mkdirSync(roslynatorConfigurationDirectoryPath, { recursive: true });
fs.writeFileSync(path.join(roslynatorConfigurationDirectoryPath, 'roslynator.config'), 'existing content');

await openRoslynatorConfiguration('roslynator.config', 'new content', appDataPath);

const openFileContent = vscode.window.activeTextEditor?.document.getText();

assert.equal(openFileContent, 'existing content');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as assert from 'assert';
import * as fs from 'fs-extra';
import * as path from 'path';

import * as roslynator from '../../extension';
import { ensureOmnisharpConfigurationUpdated } from '../../updateOmnisharpConfiguration';
import { OmnisharpSettings } from '../../omnisharpSettings';

suite('Auto update omnisharp.json', () => {
Expand All @@ -24,7 +24,7 @@ suite('Auto update omnisharp.json', () => {
});

test('Create omnisharp.json', () => {
roslynator.ensureConfigurationUpdated({
ensureOmnisharpConfigurationUpdated({
extensionDirectoryPath: path.join(extensionsPath, 'josefpihrt-vscode.roslynator-1.0.1'),
homeDirectoryPath: homePath
});
Expand Down Expand Up @@ -54,7 +54,7 @@ suite('Auto update omnisharp.json', () => {
fs.mkdirSync(omnisharpPath);
fs.writeJSONSync(omnisharpJsonPath, oldOmnisharpSettings);

roslynator.ensureConfigurationUpdated({
ensureOmnisharpConfigurationUpdated({
extensionDirectoryPath: path.join(extensionsPath, 'josefpihrt-vscode.roslynator-1.0.2'),
homeDirectoryPath: homePath
});
Expand All @@ -80,7 +80,7 @@ suite('Auto update omnisharp.json', () => {
fs.mkdirSync(omnisharpPath);
fs.writeJSONSync(omnisharpJsonPath, oldOmnisharpSettings);

roslynator.ensureConfigurationUpdated({
ensureOmnisharpConfigurationUpdated({
extensionDirectoryPath: path.join(extensionsPath, 'josefpihrt-vscode.roslynator-1.0.1'),
homeDirectoryPath: homePath
});
Expand Down
79 changes: 79 additions & 0 deletions src/VisualStudioCode/package/src/updateOmnisharpConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import * as json5 from 'json5';
import { OmnisharpSettingsKey } from './omnisharpSettings';
import { Context } from './context';

export function ensureOmnisharpConfigurationUpdated(context: Context) {
const omnisharpDirectoryPath = path.join(context.homeDirectoryPath, '.omnisharp');

if (!fs.existsSync(omnisharpDirectoryPath)) {
fs.mkdirSync(omnisharpDirectoryPath);
}

const omnisharpJsonPath = path.join(omnisharpDirectoryPath, 'omnisharp.json');

let omnisharpSettings: any;
let settingsUpdated = false;

if (fs.existsSync(omnisharpJsonPath)) {
const omnisharpJson = fs.readFileSync(omnisharpJsonPath, { encoding: 'utf8' });
omnisharpSettings = json5.parse(omnisharpJson);
} else {
omnisharpSettings = {};
}

const roslynExtensionsOptionsKey = findKey(omnisharpSettings, OmnisharpSettingsKey.RoslynExtensionsOptions);

if (!omnisharpSettings[roslynExtensionsOptionsKey] || typeof omnisharpSettings[roslynExtensionsOptionsKey] !== 'object') {
omnisharpSettings[roslynExtensionsOptionsKey] = {};
}

const enableAnalyzersSupportKey = findKey(omnisharpSettings[roslynExtensionsOptionsKey], OmnisharpSettingsKey.EnableAnalyzersSupport);

if (omnisharpSettings[roslynExtensionsOptionsKey][enableAnalyzersSupportKey] !== true) {
omnisharpSettings[roslynExtensionsOptionsKey][enableAnalyzersSupportKey] = true;
settingsUpdated = true;
}

const locationPathsKey = findKey(omnisharpSettings[roslynExtensionsOptionsKey], OmnisharpSettingsKey.LocationPaths);

if (!Array.isArray(omnisharpSettings[roslynExtensionsOptionsKey][locationPathsKey])) {
omnisharpSettings[roslynExtensionsOptionsKey][locationPathsKey] = [];
}

const roslynPath = path.join(context.extensionDirectoryPath, 'roslyn');
const locationPaths = [
path.join(roslynPath, 'common'),
path.join(roslynPath, 'analyzers'),
path.join(roslynPath, 'refactorings'),
path.join(roslynPath, 'fixes')
].map(p => p.replace(/\\/g, '/'));

const containsPaths = locationPaths.every(
p => (omnisharpSettings[roslynExtensionsOptionsKey][locationPathsKey] as any[]).includes(p));

if (!containsPaths) {
const unrelatedPaths: string[] = (omnisharpSettings[roslynExtensionsOptionsKey][locationPathsKey] as any[])
.filter(p => typeof p === 'string'
&& !p.includes('josefpihrt-vscode.roslynator') && !locationPaths.includes(p));

omnisharpSettings[roslynExtensionsOptionsKey][locationPathsKey] = [
...unrelatedPaths,
...locationPaths
];
settingsUpdated = true;
}

if (settingsUpdated) {
const updatedOmnisharpJson = JSON.stringify(omnisharpSettings, null, 4);
fs.writeFileSync(omnisharpJsonPath, updatedOmnisharpJson);

vscode.window.showInformationMessage('omnisharp.json has been updated with Roslynator configuration.');
}
}

function findKey(settings: any, key: OmnisharpSettingsKey) {
return Object.keys(settings).find(k => k.toLowerCase() === key.toLowerCase()) ?? key;
}
Loading