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

Feature Request: Enable emmet for other file extensions. #9500

Closed
jixunmoe opened this issue Jul 19, 2016 · 24 comments
Closed

Feature Request: Enable emmet for other file extensions. #9500

jixunmoe opened this issue Jul 19, 2016 · 24 comments
Assignees
Labels
feature-request Request for new features or functionality
Milestone

Comments

@jixunmoe
Copy link

  • VSCode Version: 1.3.1
  • OS Version: Ubuntu 16.06

Steps to Reproduce:

  1. Install Nunjucks plugin.
  2. Type p and press tab.
  3. \t was inserted.

Expected Result:

  1. p expanded to <p></p>.

I was looking for the setting to enable emmet, but it seems hard-coded in the source (src/vs/workbench/parts/emmet/node/editorAccessor.ts#L20).

Can we modify this value through preference?

Thanks.

@fahrradflucht
Copy link

fahrradflucht commented Jul 19, 2016

I think thats an issue that comes up not the first time (#4962 or #4700 for example) but was always phrased as "support emmet in filetype x". I think implementing an option to turn emmet on for filetypes or if some people want it everywhere (thats how I have it in vim) would be nice and could solve this one and for all.

This option should be triggerable by plugins implementing new filetypes as well. Otherwise it would not be possible to fix issues like https://github.com/mat-mcloughlin/vscode-elixir/issues/11 since you can't just associate .eex with html or would it?

@jixunmoe
Copy link
Author

I did an ugly hack to force it enable:

# sed -i.bak 's/emmetSupportedModes=\["html",/emmetSupportedModes=\["nunjucks","html",/' /usr/share/code/resources/app/out/vs/workbench/workbench.main.js

Everything seems fine for now.

@kieferrm kieferrm added the feature-request Request for new features or functionality label Jul 20, 2016
@egamma
Copy link
Member

egamma commented Jul 26, 2016

@mrmlnc what would be a good way to expose this that is consistent with emmet. Now that VS Code exposes emmet preferences and profiles?

@mrmlnc
Copy link
Contributor

mrmlnc commented Jul 27, 2016

@egamma, now it is difficult to say something.

The main problem is that we have a list of language identifiers that cannot be edited and supplemented. This imposes on us the responsibility to resolve issues such as this.

Now our main problem is the isEmmetEnabledMode function.

I see several ways to solve this problem.

Very simple way

Expand the list of language identifiers use emmet.syntaxProfiles property. For example:

Editor settings

{
  "emmet.syntaxProfiles": {
    // force HTML profile for Nunjucks syntax
    "nunjucks": "html"
  }
}

getSyntax function

// Supported by Emmet languages
const baseEmmetLanguages = ['html', 'css', 'xml', 'svg', 'xsl', 'haml', 'jade', 'jsx', 'slim', 'scss', 'sass', 'less', 'stylus', 'styl'];

public getSyntax(): string {
  let position = this.editor.getSelection().getStartPosition();
  let modeId = this.editor.getModel().getModeIdAtPosition(position.lineNumber, position.column);
  let syntax = modeId.split('.').pop();

  // Pseudo code
  let syntaxProfilesLanguages = editor.settings.emmet.syntaxProfiles;

  if (syntaxProfilesLanguages[syntax] && typeof syntaxProfilesLanguages[syntax] === 'string') {
    return syntaxProfilesLanguages[syntax];
  }

  // Default checks for `typescriptreact`, `sass-indented` and other IDs
  // ...

  return syntax;
}

Pros

  • We don't need to do anything.
  • Looks decent.

Cons

  • Full responsibility rests with the end user, not the creator of the plugin.
  • Removed settings - write again.
  • We still need to have conditions for certain syntaxes. (typescriptreact, sass-indented and etc).

Best way

This method solves the problem fundamentally.

Each language has a chain of heritage in tmLanguage or language.json. Also the scope is specified in the package.json file in each language extension (contributes.grammars.scopeName). This parameter is stored in the scopeName field.

scopeName (line 1) — this should be a unique name for the grammar, following the convention of being a dot-separated name where each new (left-most) part specializes the name. Normally it would be a two-part name where the first is either text or source and the second is the name of the language or document type. But if you are specializing an existing type, you probably want to derive the name from the type you are specializing. For example Markdown is text.html.markdown and Ruby on Rails (rhtml files) is text.html.rails. The advantage of deriving it from (in this case) text.html is that everything which works in the text.html scope will also work in the text.html.«something» scope (but with a lower precedence than something specifically targeting text.html.«something»).

Documentation: https://manual.macromates.com/en/language_grammars

For example, this method is used in Atom. Thus we can 100% be sure that Emmet works for all languages, that he can support.

Warning

Unfortunately, some languages do not have this field. See code example below (razor).

For example with pesudo code:

// Supported by Emmet languages
const baseEmmetLanguages = ['html', 'css', 'xml', 'svg', 'xsl', 'haml', 'jade', 'jsx', 'slim', 'scss', 'sass', 'less', 'stylus', 'styl'];

public isEmmetEnabledMode(): boolean {
  let syntax = this.getSyntax();
  return Boolean(syntax);
}

public getSyntax(): string {
  // Pseudo code
  let scopeName = getDocumentSyntaxScopeName();

  // scope[0] -> source or text
  // scope[1] -> parent syntax
  // ...
  // scope[n] -> current syntax
  let scopes = scopeName.split('.')

  // For example:
  //
  // [1] Less -> source.css.less
  // [2] CSS -> source.css
  // [3] Java -> source.java
  // [4] PHP -> text.html.php
  // [5] ASP vb.NET -> source.asp.vb.net

  let currentSyntax = scopes[scopes.length];
  // [1] Less -> source.css.less -> less -> true -> return `less`
  // [2] CSS -> source.css -> css -> true -> return `css`
  // [3] Java -> source.java -> java -> false
  // [4] PHP -> text.html.php -> php -> false
  // [5] ASP vb.NET -> source.asp.vb.net -> net -> false
  if (baseEmmetLanguages.indexOf(currentSyntax) !== -1) {
    return currentSyntax;
  }

  let parentSyntax = scopes[1];
  // [3] Java -> source.java -> java -> false
  // [4] PHP -> text.html.php -> html -> true -> return `html`
  // [5] ASP vb.NET -> source.asp.vb.net -> asp -> false
  if (baseEmmetLanguages.indexOf(parentSyntax) !== -1) {
    return parentSyntax;
  }

  // Default checks for `typescriptreact`, `sass-indented` and other IDs
  //
  // Handlebars (hbs), erb, ejs, twig with extension?
  if (/\b(razor)\b/.test(syntax)) { // treat like html
    return 'html';
  }

  if (/\b(typescriptreact|javascriptreact)\b/.test(syntax)) { // treat like tsx like jsx
    return 'jsx';
  }

  if (syntax === 'sass-indented') { // map sass-indented to sass
    return 'sass';
  }

  // [3] Java -> source.java -> return null -> drop Emmet
  // [5] ASP vb.NET -> source.asp.vb.net -> return null -> drop Emmet
  return ''; // may be null or false ?
}

Pros

  • All languages automatically have support Emmet.
  • The end user must not do anything except install the extension.

Cons

  • A lot of code.
  • We still need to have conditions for certain syntaxes. (typescriptreact, sass-indented and etc).

Not a problem, but need to optimize language checking. In the above code checks only the current and parent syntax. But in the real case, a set of syntaxes can be unlimited (see ASP vb.NET source.asp.vb.net).

For example, Atom, checks first only the current and parent syntax and, if not, then begins to check the entire chain: https://github.com/emmetio/emmet-atom/blob/master/lib/editor-proxy.coffee#L207-L223

@egamma egamma added this to the August 2016 milestone Jul 27, 2016
@egamma
Copy link
Member

egamma commented Jul 27, 2016

@mrmlnc great analysis thanks. What is obvious is now that we support the syntaxProfiles setting the hard coded behaviour in isEmmetEnabledMode function must be removed.

I've added this on the plan for August since we are just wrapping up the July update.

@egamma
Copy link
Member

egamma commented Aug 24, 2016

@mrmlnc starting to implement this. I think we need both solutions that you described above:

  1. we should honor when the user defines a different profile for an existing language
  2. it should be possible to contribute a language to vscode that gets emmet support without that the user needs to configure emmet support in the settings.

egamma added a commit that referenced this issue Aug 24, 2016
@mrmlnc
Copy link
Contributor

mrmlnc commented Aug 24, 2016

@egamma, on one hand I have nothing against current solution (partial fix). But on the other hand, when I install extension (for example, HAML), I want to support Emmet out of the box. Also users must now have additional settings. And we still have a list of available languages.

And to make matters worse, users should be aware of the existing profiles Emmet, that are not described in the documentation.

@egamma
Copy link
Member

egamma commented Aug 24, 2016

@mrmlnc I fully agree with you. This is why I didn't close the issue yet. I'm about to check in the 2nd part of the fix which covers the NunJucks scenario.

And to make matters worse, users should be aware of the existing profiles Emmet, that are not described in the documentation.

I've filed this issue to track the documentation gap microsoft/vscode-docs#541. Is there some emmet documentation we can refer to?

@mrmlnc
Copy link
Contributor

mrmlnc commented Aug 24, 2016

@egamma the official documentation not contain a list of available profiles. It can be found in the lib/snippets.json file. Also see http://docs.emmet.io/abbreviations/types/#element-types

  • css
  • html
  • xhtml - hardcode (the same as html, but outputs empty elements with closed slash: <br />.)
  • xml
  • svg (emmet v1.4.0+ we have 1.3.1)
  • xsl
  • haml
  • jade
  • jsx
  • slim
  • scss
  • sass
  • less
  • stylus
  • styl = stylus

As array:

['html', 'xhtml', 'css', 'xml', 'xsl', 'haml', 'jade', 'jsx', 'slim', 'scss', 'sass', 'less', 'stylus', 'styl']

@mrmlnc
Copy link
Contributor

mrmlnc commented Aug 24, 2016

@egamma, small question:

First, see 2f2d650#commitcomment-18768402

Also maybe add a condition for those languages that are already present in the list? It seems to me there is no point in checking the parent languages for CSS or HTML.

    public getSyntax(): string {
        ...
        // user can overwrite the syntax using the emmet syntaxProfiles setting
        let profile = this.getSyntaxProfile(syntax);
        if (profile) {
            return profile;
        }
+       if (this.emmetSupportedModes.indexOf(syntax) !== -1) {
+           return syntax;
+       }
        if (/\b(razor|handlebars|erb|php|hbs|ejs|twig)\b/.test(syntax)) { // treat like html

IMO, you can replace emmetSupportedModes array on array of officially supported languages by Emmet and remove twig, ejs, erb from treat like html condition.

PR ? 😸

@egamma
Copy link
Member

egamma commented Aug 25, 2016

@mrmlnc thanks for the code review.

Are you sure that you correctly get the parent language?

No, I wasn't aware of this case text.html.php.laravel-blade and I assumed going one specialization level to the left is sufficient. In this case let's not call it the 'parent language' but the 'document language.' This is consistent with text mate documentation

"... first is either text or source and the second is the name of the language or document type."

However, it is really time for some unit tests for the getSyntax() code. I'll start on it.

Also maybe add a condition for those languages that are already present in the list?

👍

IMO, you can replace emmetSupportedModes array on array of officially supported languages by Emmet and remove twig , ejs , erb from treat like html condition.

💯 , I wasn't sure of the official supported list. So a PR would be much appreciated.

@egamma egamma reopened this Aug 25, 2016
@mrmlnc
Copy link
Contributor

mrmlnc commented Aug 25, 2016

@egamma, Sadly, TextMate does not limit the nesting of languages (but recommends use (source|text).(document type|language).language pattern).

As I understand from the documentation, scopeNames is formed according to the principle: On the right is a higher abstraction. For example, text.html.php.laravel-blade or source.asp.vb.net.

What do you think about replacing the last condition on cycle? It's completely cover scopeNames (see https://github.com/emmetio/emmet-atom/blob/master/lib/editor-proxy.coffee#L218-L221).

private checkParentMode(syntax: string): string {
    ...
-   let parentMode = languages[languages.length-2];
-   if (this.emmetSupportedModes.indexOf(parentMode) !== -1) {
-       return parentMode;
-   }
    return syntax;
}
for (let i = 1; i < languages.length; i++) {
    const language = languages[languages.length - i];
    if (this.emmetSupportedModes.indexOf(language) !== -1) {
        return language;
    }
}

@egamma
Copy link
Member

egamma commented Aug 25, 2016

@mrmlnc 👍 on this change, it makes the core more robust and removes an assumption.

BTW, I'm refactoring the editorAccessor code a bit to make it easier testable. So if you plan to make a PR, please wait until I'm done with this refactoring and we can have unit tests for all this logic.

@mrmlnc
Copy link
Contributor

mrmlnc commented Aug 25, 2016

@egamma,

So if you plan to make a PR, please wait until I'm done with this refactoring...

Yes, thanks for the warning. I'll wait your changes.

@egamma
Copy link
Member

egamma commented Aug 25, 2016

@mrmlnc
I've checked in unit test for the EditorAccessor, please review them and add more as needed.

@mrmlnc
Copy link
Contributor

mrmlnc commented Aug 26, 2016

@egamma, done.

@iamandrewluca
Copy link

iamandrewluca commented Dec 27, 2016

Hello folks!
I have a eRuby file myFile.html.erb and it seems that emmet does not work.
I also tried to add this to settings but no chance.

{
    "emmet.syntaxProfiles": {
        "eruby": "html"
    }
}

Also tried erb no chance.
How can I fix this?
Thanks in advance!

@egamma
Copy link
Member

egamma commented Jan 17, 2017

@iamandrewluca commenting on a closed issue can easily be overlooked. Please file a new issue for the problem you mention in #9500 (comment)

fyi @ramya-rao-a this could be related to #18434

@ramya-rao-a
Copy link
Contributor

ramya-rao-a commented Jan 23, 2017

@iamandrewluca What is the language mode that appears in the bottom right corner of the status bar when you have this myFile.html.erb file open? My guess is that it would be plain text. I don't believe erb or eruby is one of the valid language modes in VS Code out of the box.

Set the language mode for this file to html and you should be good to go

@yordis
Copy link

yordis commented Jul 5, 2017

@ramya-rao-a I am doing .vue files and I know switching to html in the Language Mode will actually fix my issue

but,

What is the consequences of that? What happen if the .vue extension have multiple processing like emmet itself have?

@ramya-rao-a
Copy link
Contributor

@yordis I don't understand what you mean by "multiple processing like emmet".

Did you try using emmet.syntaxProfiles setting to map vue to html?

@hogdogthegod
Copy link

I was also having trouble getting emmet to run on erb files. The language was mode was being recognized as erb after installing [vscode-ruby-erb](https://github.com/vortizhe/vscode-ruby-erb, and I had emmet.syntaxProfiles set to associate erb with html ("emmet.syntaxProfiles": { "erb": "html" } ).

I had to disable useNewEmmet for it to work.

@ramya-rao-a
Copy link
Contributor

ramya-rao-a commented Jul 22, 2017

@hogdogthegod In the new emmet model, we are using emmet.includeLanguages for mapping new language modes to an existing language that has emmet support.

"syntaxProfiles" in emmet was meant to configure the output profile eg: casing control on attribute name or single/double quotes for attributes etc. You can read more here: https://docs.emmet.io/customization/syntax-profiles/
So we are moving away from hijacking "syntaxProfiles" for mapping new languages

Please read https://code.visualstudio.com/updates/v1_13#_emmet-abbreviation-expansion-in-suggestion-list and https://code.visualstudio.com/updates/v1_14#_emmet-abbreviation-improvements for more information on the new emmet model

@abnersajr
Copy link

You can simple use this:

"emmet.includeLanguages": {
        "erb": "html"
    }

@vscodebot vscodebot bot locked and limited conversation to collaborators Nov 17, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature-request Request for new features or functionality
Projects
None yet
Development

No branches or pull requests

10 participants