microsoft / vscode

Visual Studio Code
https://code.visualstudio.com
MIT License
162.51k stars 28.66k forks source link

Feature Request: Enable/disable extensions from config file #40239

Open michaeljota opened 6 years ago

michaeljota commented 6 years ago

Explain:

There are certain extensions that play well together, and it would be useful to be able to set a config file to enable and disable certain extensions in that workspace. This would be a config file, like the extensions recommendations, but with a series of parameters that would allow to enable and disable certain extensions.

This would be like a config file for the "[Dis]Allow (Workspace)" setting.

KaungZawHtet commented 2 years ago

@alefragnani Yes, you r right. VS code already give manual polyglot experience. What we r discussing is automated/smart polyglot exp. As u said, profiling is the pragmatic solution right now.

alefragnani commented 2 years ago

@KaungZawHtet There is nothing manual on how VSCode works. Quite the opposite.

Extensions are activated only when necessary. The rule that defines the necessity, in this case, is defined by the extension author. ie: the C# extension won’t be activated if you open some folder containing only Markdown files.

What we need is a smart/customized experience, defined by the end-user, overriding/combining with what the extension author had in mind for the extension.

For instance, the Prettier extension activates onStartupFinished(which means right after VS Code starts, on every new window). But I only use Prettier at work, for JS/TS projects, not for C#, MD, Delphi, etc… I need a way to say that to VS Code.

I’m curious to see how Fleet works. I started to use Rider this year, but the plugin experience is not on par to VSCode. Trying to take the best of both worlds 😬

jcrben commented 2 years ago

It sounds like .devcontainer (which I just learned about from the mention at https://github.com/microsoft/vscode/issues/139409 by @sana-ajani) addresses what I want for the most part https://code.visualstudio.com/docs/remote/containers-tutorial

Surprisingly not much discussion of devcontainer as an alternative in these feature requests.

Karl-EdwardFPJeanMehu commented 2 years ago

It would be great if implemented especially with the new Multiple language specific editor settings. Imagine the ability to enable only the desired extensions based on specified languages similar to what was suggested by @ramya-rao-a !:

 {
   "[javascript][typescript]": {
          "disabled": [
          "eg2.tslint",
          "dbaeumer.vscode-eslint",
          ...
      ]
   }
 }

It would surely help prevent loading unnecessary extensions.

CyberFoxar commented 2 years ago

As someone who's coding on muliple machines, mulitple languages and sometime uses the same files but in different contexts, I would love for the "disable (workspace)" button to actually save the disabled extensions to the .code-workspace file, the workspace file.

Either that or .vscode folder.

One of my use case is switching from a set of markdown extensions that I use for writing complex things (See: using Dendron) to one that has simplier checks when I'm in a coding project.

mosesoak commented 2 years ago

@michaeljota Just want to understand what is the use case to have this in a file?

A great example is Vue's typescript setup, which can either make use of two language plugins (inefficient) or replace the built-in typescript engine with their main extension, something that they term "Takeover Mode". To use this mode you disable @builtin typescript > TypeScript and JavaScript Language Features extension.

https://vuejs.org/guide/typescript/overview.html#takeover-mode

What would be convenient here would be to have both the workspace-recommended extension and the automatic disabling of the default TS/JS language extension, something that currently has to be listed in the README as an extra step during project setup.

ChrisKader commented 2 years ago

@KaungZawHtet There is nothing manual on how VSCode works. Quite the opposite.

Extensions are activated only when necessary. The rule that defines the necessity, in this case, is defined by the extension author. ie: the C# extension won’t be activated if you open some folder containing only Markdown files.

What we need is a smart/customized experience, defined by the end-user, overriding/combining with what the extension author had in mind for the extension.

For instance, the Prettier extension activates onStartupFinished(which means right after VS Code starts, on every new window). But I only use Prettier at work, for JS/TS projects, not for C#, MD, Delphi, etc… I need a way to say that to VS Code.

I’m curious to see how Fleet works. I started to use Rider this year, but the plugin experience is not on par to VSCode. Trying to take the best of both worlds 😬

@alefragnani The drive for this feature is based on how large and plentiful the VS Code Extension ecosystem is. Some extensions work well with different projects. This is very prevalent in the example provided by @mosesoak here. In this case of vscode.typescript-language-features there could be a setting the developers could add that would allow us to disable the extensions features but it does not really solve the actual need of users overall.

An extension system like the one built into VS Code is designed around USER choice. Extension developers provide extensions for users to install but it's up to the user to decide if they want to use it. I am a bit surprised that this issue was opened in 2017 and we still do not have a way to disable extensions using the extensions.json file.

{ "recommendations": ["johnsoncodehk.volar"], "disabled":["vscode.typescript-language-features"] }

rayanth commented 2 years ago

To add another use-case for this, at my employer I use an extension to tie in to a static-code-analysis system for performing the static analysis on my primary project code-base. The extension loads in every project, however, and I often spawn new projects to perform a small task, or to test how something might work without being in the main codebase. The extension has to be dismissed and its settings modified in every new project that is not tied to the static-analysis system, or it will present an alert when it tries to compare "changes" to a remote server that it isn't configured for that project. I would prefer to be able to just disable the extension by default in all workspaces except for those which have it enabled.

karam72 commented 2 years ago

Please support this feature :heart:

TonyGravagno commented 2 years ago

Let's think about this differently. Rather than attempting to enable/disable an extension, consider skipping configuration for an undesirable enabled extension, essentially disabling it.

The initialization loop in extensionDescriptionRegistry processes package.json for each extension. It does two main things for each extension:

1: It saves extension detail in lists used elsewhere. Consider; If an enabled extension is not pushed onto these lists, it cannot be configured for other processing. Doesn't that solve the problem?

this._extensionsMap.set(ExtensionIdentifier.toKey(extensionDescription.identifier), extensionDescription);
this._extensionsArr.push(extensionDescription);

2: There is a loop to index extensions with specific activationEvents. When events occur, there is a lookup on _activationMap for extensions that need to be activated. Consider again, if the map doesn't include a specific extension, then no event "should" ever trigger the extension's activation.

So as has been discussed here, please consider a new 'allowExtensions' list in workspace settings, that is checked just before the steps 1 and 2 above.:

if(!this._extensionIsAllowedForWorkspace(extensionDescription.name))
    continue; // no entry in lists means no activation
this._extensionsMap.set(ExtensionIdentifier.toKey(extensionDescription.identifier), extensionDescription);
this._extensionsArr.push(extensionDescription);
// loop on activation events...

There are some exceptions that need to be researched. For example, there is code here that checks "delta extensions" (can't figure out what that even means), before it looks in each for activate events here. We need to prevent extensions from being added into the toAdd list, so they won't be checked for activation.

To address that and possible future additions to core that might be affected, I don't like this idea, but here it goes... Rather than removing the extension ID from _extensionsMap and _extensionsArr, leave them there, but empty the activationEvents array. This way, no events will be linked to a given extension, including "*" and "onStartupFinished".

Someone reviewing the code above might be OK with avoiding the add of an extension to _activationMap, but we might find that details must still be saved into the Maps/Arr lists. That is, what's the ramification of an enabled extension not being in those arrays? I think that will be a problem.

Another consideration - This doesn't address the loading of contributions. So even if not activated (I believe) commands will still be registered, key bindings set, and menus, views, and other assets will still be loaded. I am not familiar with where these assets are loaded but I'm guessing the same kind of solution applies, to call out for an extension filter list (add/remove) before loading.

Another consideration - I believe extension code, like extension.js still loads and consumes some resources before activate() is invoked. While they should not do "too" much, what they actually do is subject to change from one developer to the next. So the argument about unused extensions consuming resources is still marginally valid.


Is there something missing from this analysis?

Aside from looking deeper for exceptions that don't use the above code before processing extensions, what invalidates this approach?


Here is a play on that approach to show it's not only effective now, but it's extensible:

If we can force an extension to load earlier, we might be able to insert a pre-activation hook. Just before activation of any extension (via extHostManager.activate() ref1,2), invoke a new hook/command, passing in the current list of extensions to be activated, and getting a filtered subset that's missing currently prohibited extensions. An extension that's registered that command can use custom .json file in the project, or calculate which extensions are not required at runtime based on project characteristics. I know there is the SortBucket enum to prioritize extension loading (Builtin = 0, User = 1, Dev = 2, with each bucket sorted by folder name). But as in some other environments, might there be a way for User extensions to assert a higher priority over others?

With the above, let's say core doesn't just pass a list of extensions to a user extension for filtering. Let's say it passes the entire extensionsMap with all of the extensionDescription arrays, and it accepts back the same arrays. Then an external plugin (or another core module) can filter specific activationEvents. That is, I don't mind the FooJSON extension activating if I use one of its registered commands, but I don't want it to load every time there is a .json file in my project folder. I might want it to load only when there is a specific .json file, or if a specific .json file has a specfic element, or when there is some other extension present, or when I do something else in the environment that issues a command that I want to register to activate that extension.

A possible issue with that last proposal is that proper extension behavior might depend on when it's loaded, so assume we follow that pattern (pseudo-code)

extensionDescription = vscode.commands.executeCommand(
           'vscode.hookExtensionActivation',extensionDescription);
// was an activationEvent stripped out or changed? might be important, or not...
this._extensionsMap.set(id, extensionDescription);
this._extensionsArr.push(extensionDescription);

Summary: 1) Don't disable, just don't activate. 2) As always, use workspace settings for a list of extensions to allow and/or disallow. 3) Bonus: Rather than just using a hardcoded list, consider a callout that allows other extensions to determine whether there is an activation. 4) This is not a complete solution, but it's arguably better than what we've had for the last several years that this has been on the table.

Thanks for your time.

scvnc commented 2 years ago

My use case for this: we have Vue2 and Vue3 apps in our organization. If on a Vue2 project, we are familiar with using the Vetur extension. For Vue3 projects you have to use Volar, which definitely conflicts with Vetur.

It would be nice for project onboarding if vscode could be like "hey, you have Vetur enabled but the workspace indicates that it should be disabled for this workspace, would you like to disable it?"

For now, will have to just indicate this in the setup readme: "Install all recommended vscode extensions and make sure to disable Vetur for this workspace if you have that installed."

ChrisKader commented 2 years ago

Let's think about this differently. Rather than attempting to enable/disable an extension, consider skipping configuration for an undesirable enabled extension, essentially disabling it.

The initialization loop in extensionDescriptionRegistry processes package.json for each extension. It does two main things for each extension:

1: It saves extension detail in lists used elsewhere. Consider; If an enabled extension is not pushed onto these lists, it cannot be configured for other processing. Doesn't that solve the problem?

this._extensionsMap.set(ExtensionIdentifier.toKey(extensionDescription.identifier), extensionDescription);
this._extensionsArr.push(extensionDescription);

2: There is a loop to index extensions with specific activationEvents. When events occur, there is a lookup on _activationMap for extensions that need to be activated. Consider again, if the map doesn't include a specific extension, then no event "should" ever trigger the extension's activation.

So as has been discussed here, please consider a new 'allowExtensions' list in workspace settings, that is checked just before the steps 1 and 2 above.: ...

@TonyGravagno, I can understand the issue of having an option in the settings.json file that can disable an extension as that file is loaded after extensions are, if I remember correctly. I do not think it would be a good idea to have two means of "disabling" an extension.

However, in the instance of devcontainers (where I think this would be most widely used as to help control dev environments), having something in the devcontainer.json file should not be an issue.

I have tried pretty hard to figure out where vscode-server persists data for extension enablement/disablement so I could just have a bash script that updates the file with the extensions I want.

TonyGravagno commented 2 years ago

Follow-ups to @ChrisKader : First, thanks for your feedback!

the settings.json file that can disable an extension as that file is loaded after extensions are, if I remember correctly.

If that is accurate, please ref my notes about the SortBucker, which would allow another extension to load before others, and for a file other than settings.json to load early in the startup cycle.

having something in the devcontainer.json file should not be an issue.

It is an issue for those who don't use containers. Binding this concept of VSCode extension enablement with containers or any other external dependencies is inappropriate and unnecessary - with respect, it's just passing on the problem to some other entity just to get rid of it. At the most basic level, I'm sure we'd see statements like : "you mean I need to setup a container system just to use a text editor?" along with decades-old laments about how Microsoft does things. There's no need to go there.

so I could just have a bash script that updates the file with the extensions I want.

While unlikely, it's possible that someone might want an extension enabled/active in one instance and not another. Disabling at the the level you suggest precludes that ability. Rather than using a bash script, I think it would be more standard if we could do this in tasks.json or (better) with community extensions that offered different approaches to solve the problem. Although I have nothing against scripting, use scripts when required to get around minor VSCode nuances, and would welcome that as a temporary solution to this years-old challenge.

fourpastmidnight commented 2 years ago

As a follow-up to my suggestion from at least a year ago, if not older, I'm going to restate/reframe this request as I have seen it after re-reviewing this issue thread in its entirety.

In the below, I'm going to use the terms enable and disable frequently. These terms are ambiguously used in some cases. For example, when saying enable, depending on the context, it may be more appropriate to mean loaded or activated. For example, when processing which extensions ought to be "disabled" by VS Code when opening a workspace, it may be appropriate to either:

Since it's obvious I don't understand the semantics around what a "disabled" extension may mean, I use the terms enable and disable generically, but it may be more appropriate depending on the context for the extension to simply not be activated, or even loaded into memory.

The main themes of this issue are:

  1. Users find VS Code to be a great editor and love the plethora of extensions within the ecosystem
  2. Users work on a variety of different projects from a technology stack perspective. I.e. one workspace may be a PowerShell module, another a React JavaScript app, and a different one is a React TypeScript app, while another is Angular or View. Let's not forget about Go, or C#, or Unity projects, or C++, or .... You get my point. And yes, there are users who work on such a variety of projects--or even projects which contain one or more "child projects" of the types just mentioned (or others I haven't!).
  3. There are some extensions that users install and want active at all times.
  4. There are some extensions that projects would like to be installed and enabled in order to contribute to the project
  5. There are some extensions that projects would like to be disabled in order to contribute to the project (I'm thinking primarily of formatters and linters here.)
  6. There are some extensions that projects would like to suggest are helpful for working on that project which a user may wish to install and enable if it is not already installed or enabled.
  7. Yes, Visual Studio Code is supposed to only activate extensions based on the "types" of files/projects in use. BUT, this requires the extension developer to author that as part of their extension and it is a well-known fact that oftentimes this does not occur.
  8. Yes, Visual Studio Code allows users to manually enable/disable extensions via the UI for VS Code itself or per workspace. But this is a manual process that developers would need to undertake for each project opened in Visual Studio Code (thinking especially of projects users would open for the first time).
  9. Yes, you can create a Visual Studio Code extension pack. But really, are you going to create an extension pack for each and every project you would ever conceivably work on? Especially if you only ever work on it once in a rare while?
  10. Yes, Visual Studio Code allows an individual developer to specify those extensions which they would like to have enabled/disabled per VS Code window and per "project". But this is clearly intended to be a user preference as opposed to a project preference.
  11. Yes, Visual Studio Code has Dev containers. That's great!...for those of us doing development in containers. For the rest of us mere mortals who still work locally on our machine's local storage, this is a non-starter. Especially since Docker for Windows is no longer free to use for corporations! (OK, sure, you don't need Docker for Windows, but really, let's face it....)
  12. It is indisputable that the more extensions you have installed (and enabled), and the more Visual Studio Code windows you have open, the higher the CPU and Memory consumption resources of Visual Studio Code.
  13. It is indisputable that there are extensions that conflict with one another leading to Visual Studio Code instability and even crashing, and on rare occasions, corruption of your Visual Studio Code user data.

So how can we have our cake and eat it, too? The core requirements to satisfy his feature request are as follows:

  1. Users should be able to install any extensions they desire and enable/disable them as they see fit.
  2. The maintainers of workspaces should be able to require that certain extensions are required to be enabled (which obviously implies that they must also be installed) in order to contribute to a project. These will be known as Required Workspace Extensions.
    • Though a workspace may declare that it requires an extension to be present and enabled, it's just that, a declaration.
    • If a user ultimately decides that they do not want to install/enable required extensions, then the user may not be able to contribute to the project. In the end, the user makes the choice.
  3. The maintainers of workspaces should be able to require that certain extensions are required to be disabled. These will be heron further known as Disallowed Workspace Extensions.
    • Though a workspace may declare that it requires an extension to be disabled, it's just that, a declaration
    • The user should be prompted to allow the VS Code, through the declaration of the workspace, to disable the extensions. Again, the user is in control.
  4. The maintainers of workspaces should be able to suggest extensions that are not required but which may otherwise be helpful/useful when contributing to the project. These are Recommended Workspace Extensions.
  5. The maintainers of workspaces should be able to suggest extensions that, while not being disallowed, are suggested to be disabled when contributing to the project. These will be hereon further known as Not Recommended Workspace Extensions. (It's important to disambiguate that the extension in and of itself is not "not recommended", but that the workspace recommends the extension not be used with this particular workspace.)
  6. Users should be able to declaratively specify that extensions be enabled per project as they see fit, so long as these extensions do not conflict with Disallowed Workspace Extensions. If the extension is a member of the Not Recommended Workspace Extensions, the user should be prompted to confirm whether the extension ought to be enabled (and this fact recorded in some way so the user is not prompted every time the workspace is opened!)
  7. Users should be able to declaratively specify that extensions be disabled for a given project as they see fit, so long as these extensions do not conflict with Required Workspace Extensions. The user should be warned that disabling the extension would limit their ability to contribute to the workspace.
  8. These configurations are to be evaluated and processed prior to any extension being enabled/loaded/activated by Visual Studio Code with the intent being to:

    1. Reduce resource requirements for Visual Studio Code to the bare minimum necessary for the given project.
    2. Improves stability of Visual Studio Code since only a small subset of extensions will need to be loaded/activated when a project is opened.
    3. Improves responsiveness of Visual Studio Code
    4. Reduce the possibility that an extension makes it difficult to contribute to a project because either its effects are unwanted in the project, or the effects of one or more installed extensions conflict with other installed extensions
    5. Provides project maintainers the ability to communicate with developers the intended development environment for a project (as far as the project code is concerned, e.g. formatting, linting, launch settings (i.e. for running unit tests or properly setting up a debugging environment), common task settings for the local development build process, etc.)
  9. This feature really only makes sense in the context of projects which are actual VS Code workspaces. When working with a simple folder of code and other resources, there's no notion of "shared" vs. "individual" settings. However, a workspace implies a bit more structure to the project and allows us a convenient place to specify "shared" vs. "individual" settings.

In order to satisfy the goals stated above, the feature would need to allow users to intuitively and declaratively maintain one or more lists of extensions that should be enabled or disabled when Visual Studio Code is opened and/or opens a project through both a text-based interface as well as a graphical user interface.

When a user goes to add/install new extensions, additional considerations need to be made before the extension is enabled:

The end result is that extensions are ultimately maintained in textual files. The user has complete control over the "extension experience". The .vscode folder unambiguously becomes a folder which is intended to NOT be checked into source code control and contains user-defined extensions which ought to be enabled for that workspace (which aren't explicitly disallowed by the workspace), custom launch configurations, and tasks. Whether or not the <ProjectName>.code-workspace file contains all the settings for the workspace, or there is another folder (e.g., .vscode-workspace), that location would detail the list of required, recommended and disallowed extensions, project launch configurations, and project-specific tasks (i.e. build, test, publishing tasks), etc.

User-globally installed extensions are stored in a text file in the user's VS Code profile (e.g. probably not settings.json, but maybe a profile-specific extensions.json) which details whether the extension is enabled or not whenever a VS Code window is opened.

I'm sure I've missed a detail or two 😉 , but I think this captures the feature request that I think would probably meet 80% of users' needs. I welcome constructive praise and criticism.

TonyGravagno commented 2 years ago

With sincere appreciation for a well-considered post by @fourpastmidnight, personally I'd like to see a simple solution to the challenge in the short term, with a better long-term solution. Compare that to waiting more years for a re-engineering in this area that will require a significant PR and many more eyes.

While I've never done a fork/build of VSCode, I'll try to find time and use the instructions available to make the very few changes that I suggested above to create a prototype. I expect about 10 minutes of code and 3 days of struggles with VSCode as a project. :)

marke-apexit commented 2 years ago

I see various interpretations of this feature requirement, some misunderstanding, and some overcomplication in this entire thread.

I saw two recurring scenarios for this feature request:

  1. is to minimize the number of enabled/loaded extensions for a folder/workspace to reduce the overhead/ footprint of VSCode for a given project that may not need all the extensions
  1. identify the minimum set of required extensions for a given folder/workspace

I agree with the sentiment of many who have said essentially, "don't mess with a user's personal choice of extensions." Developers should be able to use whatever extensions they find useful and make them productive for a given project.

CyberFoxar commented 2 years ago

@marke-apexit , to expand on 2:

2-bis. Prevent extension conflicts when working on multiple similar projects for which multiple extensions might fit but only one is needed. That's my use case (see https://github.com/microsoft/vscode/issues/40239#issuecomment-1001166725) as well as @scvnc (in https://github.com/microsoft/vscode/issues/40239#issuecomment-1068237513) .

Tanimodori commented 2 years ago

The "Settings Profile" feature added in 1.67.0 finally has solved the issue after 5 years since its open. It exports setting as well as extension information to a "profile file" which you can import later. When you import a profile, the extension "enablement" is reset to the same state as when you have exported. Note that you still need to import the profile manually when you open a workspace, but it's a huge step advance. 🎉🎉🎉

karam72 commented 2 years ago

@Tanimodori can you please give the doc/announcement of feature please?

gjsjohnmurray commented 2 years ago

@karam72 https://code.visualstudio.com/updates/v1_67#_settings-profile

sana-ajani commented 2 years ago

Hi there! We put it in the release notes under "Preview" features. Would love for folks to try out importing/exporting a profile and give us feedback.

Some things we're working on still:

alefragnani commented 2 years ago

Just tried right after the release, and it looks promising. Congrats!

By “adding multiple profiles and switching between them”, do you mean store the profiles in VS Code storage, instead of external files via export/import?

In my small test, I created two issues (feature requests):

But, my main need for profiles feature is per window support, which means that I could have different profiles for each VS Code window. Right now, this is not possible, specially the installed/enabled extensions. This is already being requested in #57548

Thank you

TonyGravagno commented 2 years ago

This seems to be really close to what we've been discussing - and then some. Questions:

If this is all still manual, why wasn't the next step taken to add a settings_profile field to the workspace profile, and then autoload the settings profile using that field? I feel greedy as hell but it seems that would be the next thought. If this is high on the agenda, please accept my apology for sounding like a demanding "customer" when I know full well (and appreciate) how FOSS works.

I really appreciate this - I just want to make sure I/we thoroughly understand what we have now and the intent. Thanks!!

alefragnani commented 2 years ago

Really sad that #148829 was closed and tagged as extension-candidate. Forcing the development of a new extension simply because the team apparently forgot to use tabs and line breaks while saving the .code-profile file seems too much.

It took 4 years to this feature finally land on VS Code. The expectation was huge and it looked promising while testing, but right now, I’m a bit disappointed 😞 .

I really hope things could change in the future. Profiles support is the feature I needed most in VS Code.

zachhardesty7 commented 2 years ago

sorta off topic, but wrote a quick Node script that I figured I'd share in case anybody finds it useful. It lists out the currently enabled extensions by parsing this .code-profile file. It was surprisingly difficult to get a list of enabled extensions that you can copy paste to share. gist link

// run like this:
// node ./parse-vscode-profile.js "$HOME"/Downloads/profile.code-profile

const fs = require("fs")

const file = fs.openSync(process.argv[2], "r")

for (const line of fs.readFileSync(file, "utf8").split("\n")) {
  const enabledExtensions = JSON.parse(JSON.parse(line).extensions).filter(
    (extension) => !extension.disabled,
  )

  const extensionIds = enabledExtensions.map((extension) => extension.identifier.id)

  console.log(
    `🚀 ~ file: parse-vscode-profile.js ~ line 13 ~ extensionIds`,
    extensionIds,
  )
}
sana-ajani commented 2 years ago

Thanks for the feedback in this issue. The current experience is only an MVP, we are continuing to tackle some of these questions and suggestions as we refine the experience. Please see this issue for what we are prioritizing in each iteration: https://github.com/microsoft/vscode/issues/139409#issuecomment-1149245122

If you are open to chatting with our team about this experience, please reach out to me on Twitter and we can set up a time together

rivy commented 2 years ago

Here's a script that converts a '*.code-profile' file into a normal JS object (modified from @zachhardesty7's script) ...

// `node vsc-profile-to-JSON.js CODE_PROFILE`
// * convert VSCode CODE_PROFILE file to JSON

const fs = require('fs');

const file = fs.openSync(process.argv[2], 'r');

const profileText = fs.readFileSync(file, 'utf8').split('\n');
const profileTextAsJSON = JSON.parse(profileText);
const profileProps = Object.keys(profileTextAsJSON);

const profile = {};

// de-JSON-stringify profile properties
profileProps.forEach((prop) => {
    profile[prop] = JSON.parse(profileTextAsJSON[prop]);
    // * fixup double-wrapped 'settings' entry
    if (prop === 'settings') profile[prop].settings = JSON.parse(profile[prop].settings);
});

console.log(JSON.stringify(profile, null, 2));
richie5um commented 2 years ago

And, to complement that, I think, here is a script that converts a JS object (of the profile) back into a profile - which you can then re-import into VSCode. Caveat emptor :).

const fs = require('fs');

const file = fs.openSync(process.argv[2], 'r');

const profileText = fs.readFileSync(file, 'utf8');
const profileJSON = JSON.parse(profileText);
const profileProps = Object.keys(profileJSON);

const profile = {};

// // Disable All Extensions
// profileJSON.extensions.forEach((extension) => {
//     extension.disabled = true;
// });

// JSON-stringify profile properties
profileProps.forEach((prop) => {
    // * double-wrapped 'settings' entry
    if (prop === 'settings') profileJSON[prop].settings = JSON.stringify(profileJSON[prop].settings, null, 2);

    profile[prop] = JSON.stringify(profileJSON[prop]);  
});

console.log(JSON.stringify(profile));
TonyGravagno commented 2 years ago

At this point I have to admit that I'm "sort of" satisfied that we have a solution to this ticket - the ability to enable/disable extensions from config file. It is not yet elegant, @sana-ajani confirms it's just an MVP so far. We do now have the ability to do what we want, it's just not usable except with code or inelegant text poking, which is rather awkward.

For anyone catching up here, wanting some insight into this for an extension or scripted DIY

Use F1 > Settings: Profile Export, and create profile.code-profile. Edit that JSON file and note all of the \\\r\\\n, quoted (curly) braces and (square) brackets, and similar cruft. That's stringified JSON object. Regex the junk out and in a few minutes you'll have a nicely formatted JSON object (array). Note the "extensions" node where each object has an "identifier" object and a "disabled" value, referenced in the above code by @richie5um.

That code contributed here by @zachhardesty7 @rivy and @richie5um should programmatically get you to that exact same point. And as noted in Rich's script, we can simply change that "extension.disabled" property for desired extensions, resave the stringified JSON, re-Import the file, and we have the solution to this ticket: The ability to enable/disable extensions from a config file. (Gosh, at least I hope so.)

To summarize that process for an extension

1a) Invoke an export of a .code-profile file. 1b) Read and JSON.parse the stringified JSON into an object. 2) Do whatever you want - this is what we asked for the ability to do. 3a) Stringify the JSON and write it back. 3b) Invoke an import of the .code-profile file.

Steps 1 and 3 are template code which can be put into gists for import into extensions.

For step 2, here's one possible implementation approach: Write a list of extensions into the user or workspaces settings like this:

"extensions: {
   "enabled": [ "this", "that" ],
   "disabled": [ "foo", "bar"]
}

Now in your step 2 above, get that config data which I believe should have been exported in .code-profile, and set extension.disabled per the config.

Stringified JSON?

As noted by @sandy081

This is not expected to be human readable - May I know the need ?

And as noted here and in #148829 by @alefragnani (thanks to him for the Bookmarks extension I see in my "extensions" array), some of us don't want a requirement to use code on a JSON file. As with most other configs we want to edit our config file and save it for immediate (or post restart) changes to our environment. We do this all the time. It's a recognized, fast, efficient practice. So why did the developer coding this decide all of a sudden to make a profile file "machine-readable and semi-efficiently compact"? (Still has unnecessary whitespace")

For this new concern, I request #148829 to be re-opened with a proposed solution that on Export we should be able to specify the format: Stringified, or JSON Object. I believe this should just cause a skip of the code that does the stringify operation? Personally don't care about tabs, spaces, or EOLs. Yeah, compressed data is good and we can beautify it on our own. Just don't stringify it. Then we can easily open it in a window, beautify it with a keystroke, and we have a base solution to this ticket and others.

To be clear, the Import should also check to see if data is not stringified before integration of the data. At this time, the Import is not prepared to import non-stringified data and such an import fails.

Export/Import vs API or editor?

We have a "sort of" acceptable solution here, because we shouldn't need to export a config file, change it, then import it. that's completely non-standard and time-inefficient. We should be able to modify the configuration in an editor window like we do all others. Or through the API at workspace load time.

I get that the whole Profile thing is new, and I'm appreciative. We understand that APIs (and documentation) usually come way after code and the MVP. I think that until we see some solid word on improving the usability and UX, that no one who uses this software is going to feel like we have an actual usable feature given all of the great work that has been put into this.

Summary

I think we need these three items on the roadmap (#139409) to keep this moving forward.

This will allow us to maintain Profiles manually and with code. When we all have a solid handle on this, a config UI would be nice, but for most of us nerds it's probably not even necessary.

Thanks as always for time and consideration.

alexandreleduc commented 1 year ago

@michaeljota Just want to understand what is the use case to have this in a file?

Here's a use case: Whenever we get a new hire where I work, they setup their VS Code and the first thing they do is install Prettier. Prettier formatting goes against our internal formating guidelines (enforced by ESLint). We do our best to tell them not to use Prettier, but trying to get some programmers to comply is like trying to herd cats. If we could disable extensions at the project config file level, I wouldn't have to worry about Prettier reformatting the whole code base and messing up the git blame on each line.

KaungZawHtet commented 1 year ago

Now we have profile feature. This should be closed.

Avasam commented 1 year ago

Now we have profile feature. This should be closed.

I can't find the documentation about code profiles and how to apply them to a project. So my understanding of them and how they apply to this issue is limited. Can you save a profile to be used by default in the workspace (not having to import anything or run VSCode from command line)? Will devs be able to use extensions and user-specific settings not specified in the profile? Can a profile be updated by maintainers so that contributors are prompted to disable any extension that was added to a disallow-list?

DiegoBM commented 1 year ago

Any plans on creating a visual interface for this feature? Maybe there is already an extension that can get your current profiles folder from the settings and list all your available profiles for you to apply one? Although ideally vscode should probably store internally all the profiles in some form of settings, instead of keeping them in external files, so that it makes it easier to switch profiles or see which profile currently active for the current repository. Just ideas though...

EDIT: I just noticed that all this is already implemented on the insiders release, so please ignore this post https://github.com/microsoft/vscode/issues/116740

BkunS commented 1 year ago

@michaeljota Just want to understand what is the use case to have this in a file?

I know it's an old thread, but I came across with this when I search for the exact functionality and it's still open.

I was trying to setup Nuxt 3 Vue project and in its docs, it recommends Volar plugin with "take over mode" enabled, and provides a link to Vue official docs to guide people how to disable (workspace) a built-in plugin. They're pretty complicated steps and I don't think all project's collaborators will even know these and follow the same steps. I believe this is one of the use cases that could save others some time and ensure the code is behaving exactly the same across all collaborators.

JJBocanegra commented 1 year ago

I was trying to setup Nuxt 3 Vue project and in its docs, it recommends Volar plugin with "take over mode" enabled, and provides a link to Vue official docs to guide people how to disable (workspace) a built-in plugin. They're pretty complicated steps and I don't think all project's collaborators will even know these and follow the same steps. I believe this is one of the use cases that could save others some time and ensure the code is behaving exactly the same across all collaborators.

@BkunS I came here exactly for the same Volar issue, I don't think that some people in our company will be able to follow the steps and it's better to have this automatically done by a config file.

Cyclodex commented 1 year ago

I am also searching a solution (good developer experience), so that the other devs in the company have a much simpler starting point, not having to enable AND disable extensions, to allow a smooth experience using the "Volar take over mod" for specific projects.

Trial 1 (extensions.json) ❌

From somewhere, I found that one can define recommendations and also unwantedRecommendations within the .vscode/extensions.json, this however seems to only suggest the recommendations. Nothing really happens with the unwantedRecommendations...

.vscode/extensions.json

{
    // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
    // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp

    // List of extensions which should be recommended for users of this workspace.
    "recommendations": [
        "vue.volar",
    ],
    // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
    "unwantedRecommendations": [
        "octref.vetur",
        "vue.vscode-typescript-vue-plugin",
        "vscode.typescript-language-features"
    ],
}

Trial 2 (using command code to start vscode) 🤔(partially✅)

I found a documentation page about vscode extensions and how to manipulate several things using the command line. It defines things like installing or uninstalling extensions. or disabling ALL extensions...

But there is nothing mentioning that one could disable or enable extensions for a editor session. I just tried out, if I could disable an extension, instead of all... And surprise, something happened...

Hint: The following commands where executed in a project root directory, you can define what to start replacing the .

Installing an extension

code --install-extension vue.volar

This command however, seems not to be able to start the VSCode editor, it will just install the extension. It will bring a warning if the extension is already installed: Extension 'vue.volar' v1.0.22 is already installed. Use '--force' option to update to latest version or provide '@<version>' to install a specific version, for example: 'vue.volar@1.2.3'.

So depending on your needs , you might want to add the --force argument as well.

Disabling an extension

I just tried, if the argument --disable-extension ... would work as one could expect...

code . --disable-extension octref.vetur

😲 works

A new windows with VSCode starts up, having the mentioned extension disabled. When you visit the extension in the extensions explorer, it says "This extension is disabled by the environment." 🎉 image

Disabling multiple extensions

To make multiple extensions disabled, it looks like we need to add as many extensions including the argument: code . --disable-extension vscode.typescript-language-features --disable-extension octref.vetur --disable-extension vue.vscode-typescript-vue-plugin

😲 works

image

One of the extensions is not installed, that's why it is also not disabled on the screenshots

Possible Vue 3 extension setup solution

A possible setup for Vue3 / Nuxt 3 using Volar and the "take over mode" could be:

So for now, I coupled the install and starting vscode commands (with disabled extensions) together, feel free to separate or extend them as needed:

code --install-extension vue.volar; code . --disable-extension vscode.typescript-language-features --disable-extension octref.vetur --disable-extension vue.vscode-typescript-vue-plugin

Simplify it

For now, I could make a Windows-Shortcut, which executes both commands like above. We could also make a Powershell script, or any other kind of script to do the same.

StartInVSCode.ps1

code --install-extension vue.volar;
code . --disable-extension vscode.typescript-language-features --disable-extension octref.vetur --disable-extension vue.vscode-typescript-vue-plugin

A developer could then use this shortcut to start the project in VSCode

Summary / Outcome

I don't like it fully yet, because one has to start the project always with these arguments / shortcut. If you open the project directly from VSCode, which a dev probably will do, it will not disable the extensions anymore.

Therefore the best way is still that a dev will follow the given instructions, and disable the given extensions manually - to be sure they are disabled for the workspace.

Feedback / Inputs

What is your experience, does this also work for you? Could be a temporary solution for some of us. Let me know if you like it or not ;)

Investigation Sponsored by GARAIO AG

muzaisimao commented 1 year ago

Hi, @Cyclodex

About a year ago, I had the idea of using the "code" command and developed an extension to try to solve this problem, but the results were not good.

The main problem is that there are some restrictions on running the "code" command in VS Code to re-enter the current folder, and even with some additional configuration, but the extended experience is not good.

If you are interested, you can try it out. this is VS Code extension - Disable Extensions and Github Repository

As far as I know, several other developers have released extensions to solve this problem, but there is still no perfect solution.

Cyclodex commented 1 year ago

@muzaisimao Hi thanks for your feedback, interesting! Will give it a try and test it a bit.

Question: How did you found out about this command? --disable-extension, it seems not to be documented anywhere right? :)

muzaisimao commented 1 year ago

@muzaisimao Hi thanks for your feedback, interesting! Will give it a try and test it a bit.

Question: How did you found out about this command? --disable-extension, it seems not to be documented anywhere right? :)

@Cyclodex Yes, it is not documented in the official docs, I saw it in the help when I ran the "code -h" command in the terminal, and found its detailed introduction in the changelog.

Changelog is here Disabling extensions from command line

TonyGravagno commented 1 year ago

Interesting recent notes... We can open the terminal window, cd to a folder, and use "code -r ." to open that folder in the current instance of VSCode, rather than opening a new window.

So what would happen with a Task executed on workspace startup or via a hotkey, that opens the terminal window and (can we do this?) executes code -r . --disable-extension vscode.typescript-language-features ... ? The executing task would probably be terminated, brutally, or with a prompt. We probably don't care.

The big issue here is that now we're opening a folder and not a workspace, and we lose all of those valued settings.

But code --help shows there is a "--profile workspacefilename" option. I haven't gotten that to work:
code --profile D:\path\to\my-project.code-workspace

Final "I wonder if this could work" :

code -r --profile D:\path\to\my-project.code-workspace --disable-extension foo --disable-extension bar

gjsjohnmurray commented 1 year ago

I wonder if the --profile option relates to https://code.visualstudio.com/updates/v1_74#_profiles

TonyGravagno commented 1 year ago

I wonder if the --profile option relates to https://code.visualstudio.com/updates/v1_74#_profiles

You're right. My bad. code --help shows:

Opens the provided folder or workspace with the given profile and associates the profile with the workspace. If the profile does not exist, a new empty one is created. A folder or workspace must be provided for the profile to take effect.

I was reading that wrong. The syntax is : code -r path\to\foo.vscode-workspace and --profile is only related to a profile for that workspace.

So this does work:

code -r path\to\foo.vscode-workspace --disable-extension foo

But I haven't yet been able to execute a command like that from a task. I'm guessing this is related to the running synchronous terminal process not allowing its own termination with a new process. Removing -r didn't help. Perhaps use of cmd.exe or some PS insight would help. I haven't gone too deep down this rabbit hole.

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Open Refined Workspace",
            "type": "shell",
            "command": "code -r D:\\Development\\VSCode\\Workspaces\\foo.code-workspace --disable-extension bar",
            "problemMatcher": []
        }
    ]
}
Cyclodex commented 1 year ago

Hi @Tanimodori

So this does work:

code -r path\to\foo.vscode-workspace --disable-extension foo

What do you mean with "this does work?" In which situation does this disable the extension for you having the workspace open?

This only works for me, when the workspace was not open yet. When the workspace / project is open, this command seems to do nothing, but bringing the focus into the project. The Extension is still not disabled.

I think this is also why @muzaisimao is using the "new window" mode, to get this working. But its super strange that the vscode opens and then closes again - making it feel like a crash.

muzaisimao commented 1 year ago

Hi @Cyclodex, @TonyGravagno

I submitted this issue to the VS Code team, you can view it #170832 or add other content.

Cyclodex commented 1 year ago

Dear all, this was pushing me, to start creating my first real vscode extension, getting some hands dirty. 😂

The goal was for me:

Result

Here is the result of todays hacking: 🎉

Screenshots

PS: I will later add some screenshots to the readme. Here a preview:

  1. When manually checking using the command and no extensions.json file exist: image

  2. No unwantedRecommendations configured within extension.json image

  3. After checking the mentioned extensions within unwantedRecommendations: image

  4. After clicking "yes": image

  5. User can now disable them manually - Disable (Workspace)! image

  6. On next restart/reload, nothing happens - all fine image

Feedback

What do you think? Try it out and give it a try. Its meant as a workaround for the moment until VSCode improves the whole situation with Profiles/Workspaces specific extensions. Please report any issues if you ran into some.

TonyGravagno commented 1 year ago

@Cyclodex quoted me and asked:

So this does work: code -r path\to\foo.vscode-workspace --disable-extension foo

What do you mean with "this does work?" In which situation does this disable the extension for you having the workspace open?

This only works for me, when the workspace was not open yet. When the workspace / project is open, this command seems to do nothing, but bringing the focus into the project. The Extension is still not disabled.

I didn't say the foo workspace was already open. You confirmed my results: When I executed that command and there was another workspace open, it worked, and in another test when there was no workspace open, it worked. Yes, I also noticed the anomaly reported in #170832, but wasn't focused on that use case.

TonyGravagno commented 1 year ago

Hmmm, I'm glad @muzaisimao created #170832 because that seems to be the cause of the failure I reported above.

I have a task in foo.vscode-workspace that executes "code -r foo.vscode-workspace". Right ... that's not working. But if that issue is addressed then my task suggestion should work as well, with no extension required.

Cyclodex commented 1 year ago

@TonyGravagno Yea sure, (but) I don't think that the real solution would be that we have to execute a task when opening a folder. (not over an extra extension, but also not over a configured task) The best would be that the workspace/folder boots up already with the configured extensions enabled/disabled. Let's hope this is somehow possible soon with the profiles or whatever might come.

TonyGravagno commented 1 year ago

@Cyclodex we agree. The situation, as I understand it, is that it was suggested that using profiles is an interrim solution for this.

OK, trying to accept that without a UI for maintenance, we discussed JSON manipulation, and had to deal with a funky .code-profile format.

Trying to avoid that farce. I suggested using the CLI via tasks - allowing a workspace to reconfigure itself. That is not working due to #170832.

I hope that puts us back on the same page.

Moving to the next idea: I wanted to experiment with a pattern of saving profiles for various purposes, and then pulling in the desired profile from the CLI (with or without task help) using the --profile parameter. Unfortunately that led to another issue, #170916. It seems there is no extension.enabled property or similar. I don't see such data anywhere in a full profile export.

So even if we accept the Profile option that's been suggested, it still won't work.

@sana-ajani referred to #139409, but that has been closed. OK, so we're creating new tickets to address specific issues. That's the way the game is played.

I don't know what else we can or should do on this. I provided core code references above. My next step on this is to see if I can create a custom build. Will be Googling for details on how to do that soon.

Cyclodex commented 1 year ago

Thanks for your feedback @TonyGravagno. Very good points, let's hope we get one step after an other :) Thanks for your efforts