Steps to Reproduce:
.vue extension.Vue icon the html icon is shown.vscode-icons provides support for Vue files by leveraging the language id but it seems that the mechanism that it's used by the icon theme API is giving priority to the default language extensions provided by VSCode.
This has an unexpected behavior as the users are not able to see the expected icon.
In the case of Vue, you can see where it is associated to html in VSCode's code in the html extension as @JimiC pointed out in vscode-icons #725.
This is the only case where the file association has to be explicit, meaning that the user has to explicitly change the file association so she can see the proper icon. That's not the case when the language id is not provided by default by VSCode.
This is clearly creating inconsistencies in the user experience thus confusing the users about what they are supposed to do.
It would be nice if this could somehow be changed to prioritize custom language ids over default ones. This is at least what we were expecting from our side as icon theme builders until we've found out that this is not how it seems to be working.
@aeschli
After a three hours debugging session, I figured out what's going wrong.
There are three factors that are causing this misbehavior.
.vue is also registered as an extension for the html language id in the internal support of html syntax highlighting as already mentioned.Although vscode issues a warning about the extension getting overwriten, it doesn't get handled, in order for the internal association to get removed from the appropriate list (nonUserRegisteredAssociations).
While iterating through the association list (in this case the nonUserRegisteredAssociations), the flow logic prevents the correct mime to be assigned, as it gets assigned to html because the iteration finds the html first and then prevents the flow to get through to vue.
My proposed solution is to handle the issue in the already coded check section.
Thanks @JimiC for the investigation and the detailed write-up!
@alexandrudima Do we have any news on this? Is it OK if I provide a PR with my proposed solution?
Appears to be in mime.ts
Actually the flow starts from https://github.com/Microsoft/vscode/blob/master/src/vs/editor/common/services/languagesRegistry.ts#L301 but the main logic is in mime.ts.
I cannot reproduce:
=> icon shows up correctly
=> language in status bar reads Vue.js

@JimiC it is not clear how to solve that though, there are 2 extensions that provide a language for an extension and the first one wins, but how do you argue that the second one should win? What are the rules?
As we have discussed this with @robertohuertasm internally, a way to solve this is to introduce an extra level in the determination logic.
Currently, the logic is:
We propose it to become:
This can be achieved by removing the internal mime association from the list if an external extension supports the same filename.
Now, I don't know the implications of this approach as I'm not familiar with the entire code base of vscode. I'm sure that there will be cases where the user may have installed more than one extension that support the same mime type. Or even cases where the external extension references the same language id as an internal one. Current examples are the rails and Flow extensions.
@JimiC I am not a big fan of "internal" vs "external" extension, to me they are all the same and the fact that they behave differently is imho debt. And since the issue exists also when installing multiple external extensions, I think the real fix is some way of configuring this per extension.
Understandable but the issue here is that the user doesn't have a "clear" way to tell vscode to override the "internal" extension. There is a workaround though explained here.
This is the problem:
foobar*.foo and *.bar*.bar=> as a user I want to control that extension foo only deals with *.foo and extension bar deals with *.bar
I see no other solution that does not add more confusion or complexity.
@aeschli @alexandrudima let me know what you think
Exactly what I'm saying. Currently, the logic determines that *.bar is handled by foo and stops there returning that foo is the mime type. What I'm proposing is that the logic keeps iterating and if it finds an extension that solely handles *.bar returns that as mime type. This also has its own complexity.
What if:
foobar*.foo and *.bar*.bar and *.alcohol?
So now you are proposing that an extension that registers only for one extension is more important than one with more extensions? Not sure about that...
Just throwing ideas and questions here. I have already mentioned that I'm not familiar with the entire code base and I don't know the implications my proposals can create.
I have to admit though that this is a "Lerna" problem and we definitely need a "Hercules" solution.
I think this is more of a UX question ("how should it behave") than a question of how it is implemented in the end 馃憤
@bpasero Thinking of it over the weekend I concluded that there are two possible behavior solutions:
built-in extension.installed extension should always be preferred over the built-in as the installed ones can be disabled by the user.In case two or more extensions support the same filename extension then it's up to the user to disable the one that creates an issue.
Appendix:
installed the extension/extensions that the user can install.built-in the built-in extension/extensions supported by vscode.@JimiC 馃憥 I would say that this should go on the level of the file extension and not entire extension. If the user is forced to disable an extension, then the user is loosing all functionality of this extension for all file extensions this extension configures. There may still be a very good reason why the extension should not be disabled.
Imagine HTML was an extension in the store: The user is now forced to disable the HTML extension so that the ASP extension works. But that means loosing all the functionality of the HTML extension for all other file types.
Then 2. is the only option.
I disagree. Solution 3. imho is: Allow to control for any extension (either built in or installed) if a file association should be made from that extension or not. In other words: Given an extension *.foo, should extension Foo provide the language features or not.
I think this is already available by setting
"files.associations": {
"*.foo": "foo"
}
Correct me if I'm wrong.
Yeah. maybe a little less obvious and hard to discover for users.
So, is this the officially proposed way to deal with those cases? Issue closed?
@JimiC no I think the official way of doing it would be from the extension itself, e.g. here:

I could maybe have a way to disable a specific association of a language with a file pattern.
And what about when it's a filename extension in a built-in extension?
@JimiC from a user point of view I see no reason why such configuration ability should not apply to any kind of extension (either built in or external). In the end I think all extensions should be treated equally.
In general extension contribution point collision is handled the following:
fs.readFile order (which is AFAIK consistent alphabetical)fs.readFile order (also alphabetical AFAIK).So assuming vscode extensions Y and X are built-in, C and D are user installed extension and A and B are extension(s) under development, the processing order of the extensions would be: X, Y, C, D, A, B.
In case of collisions, in extension points I write, I try to implement "overwrite semantics". e.g. if extension X contributes a TM grammar for typescript, then any of the extensions Y, C, D, A or B would win if they contributed a TM grammar for typescript. The same is true I think for most of the extension points. This plays out well as user installed extensions can "overwrite" contribution points of built-in extensions.
The mime detection is quite complicated, as there are so many different ways to associate a file with a language id, but, if possible, I would try to implement the "overwrite semantics": e.g. if a built-in extension associates ".html" => html and comes a user installed extension that associates ".html" => someotherlanguage I would let the user installed extension "win".
@alexandrudima isn't the consequence of this change is that if a user has an extension installed that happens to also register for *.html, suddenly all HTML support would change and the only solution is to uninstall the installed extension (that might provide something smart for another language the user is using).
Are we sure we are willing to potentially break these users (if my assumption is right)?
After talking to @alexandrudima we decided to go forward with the model of letting a user installed extension override our built in extensions.
I have added a new property userInstalled to ITextMimeAssociation for the registerTextMime method that is being used to register all languages. This property should be set to true for extensions that are originating from the extensions folder and not our installation directory.
I did not find any property on ILanguageExtensionPoint that would tell me if the extension point is contributed from a built in or user installed extension.
@sandy081 is there any chance we could add this info to extension point handler here? I think that is the only piece missing.
Yeah that information is already there in extension description. You can get it as follows
extensions[i].description.isBuiltin
@JimiC please verify from tomorrows insider build
@bpasero Confirming that behavior is as excepted in today's insider version. 馃憤
Test done with asp and volt.
@JimiC awesome thanks.
@bpasero It has been reported to us that due to this change there is an issue with the scss association (only noticeable in Code-Insiders). Using the sass-intended extension the icon association for scss gets lost. This line may cause the issue.
@JimiC can we extract this into a new issue?
@bpasero Sure.
Most helpful comment
@bpasero Confirming that behavior is as excepted in today's
insiderversion. 馃憤Test done with
aspandvolt.