microsoft / vscode

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

Opening strings such as template literals in a new buffer, synced with original #22694

Closed plievone closed 7 years ago

plievone commented 7 years ago

There are many issues asking for language support in embedded strings, such as using HTML in ES6 template literals. It seems that it will take considerable effort to get done, and are not yet on the roadmap as far as I know.

As an intermediate solution, could VS Code facilitate opening embedded strings (when in TypeScript, for example) on the side buffer and syncing it with the original string, so that one could use any language infrastructure that is already automatically in place today for that buffer (highlighting HTML, SQL, etc)? Copying strings across buffers is of course possible already (and works great!) but it is not really usable until selecting the string and keeping it in sync is mostly automatic. Maybe they could be "portals" or something. Alternatively one could use some kind of narrowing to the part in question, but it is imperative that language services would work properly for the narrowed part as they do now for a separate buffer.

Later edit: Published an extension that implements this, works great: Template Literal Editor for VS Code. Will try to remember to keep it updated when Extension API offers new possibilities (scroll and cursor sync, document tiles, etc).

jrieken commented 7 years ago

We have experimented with that, indeed @aeschli implemented the first version for JS/CSS in html using that approach but we discarded it for various reasons, esp around diagnostics for those fake documents, because keeping line/character-mappings up-to-date isn't for free, and because language services often need to know more about those contained documents, think of project associations et al.

There are many issues asking for language support in embedded strings, such as using HTML in ES6 template literals. It seems that it will take considerable effort to get done,

Yes but we won't be able to just synthesise documents from such constructs because they come in any flavour, esp template strings. Apart from documents being the wrong abstraction for this, extensions (e.g IntelliSense for template-strings) must contribute to other extensions (TypeScript) when a string is interesting for it and when not.

Our recommendation and that's what we did for JS/CSS in HTML et al is to understand the containing structure and to operate on that portion that is of interest, e.g. that one template string in a JS/TS file. The sample below that by using the TypeScript syntax tree to check if completions are request inside a template string and if the tagging function is foo. Iff so it returns some completions.

'use strict';

import * as vscode from 'vscode';
import * as ts from 'typescript';

export function activate(context: vscode.ExtensionContext) {

    vscode.languages.registerCompletionItemProvider(['typescript', 'javascript'], {
        provideCompletionItems(doc, pos) {

            const offset = doc.offsetAt(pos);
            const source = ts.createSourceFile(doc.fileName, doc.getText(), ts.ScriptTarget.Latest, true);

            let token = (ts as any).getTokenAtPosition(source, offset)
            let template: ts.TaggedTemplateExpression;
            while (token) {
                if (token.kind === ts.SyntaxKind.TaggedTemplateExpression) {
                    template = token;
                    break;
                }
                token = token.parent;
            }

            if (!template
                || template.tag.getText() !== 'foo'
                || (offset < template.template.pos && offset > template.template.end)
            ) {
                return;
            }

            return [
                new vscode.CompletionItem('bar', vscode.CompletionItemKind.Value),
                new vscode.CompletionItem('baz', vscode.CompletionItemKind.Value),
                new vscode.CompletionItem('far', vscode.CompletionItemKind.Value)
            ];
        }
    });
}

Obviously this is just some proof of concept and something real should cache the syntax trees and ideally update them incrementally and also something real needs to compute completions properly but the filter logic is that complex

screen shot 2017-03-16 at 13 03 56

plievone commented 7 years ago

Thanks for response @jrieken!

I understand that if one aims for full document coverage with imports et al, then the issue has inherent complexity, but I'm really suggesting a partial solution or a workaround here where users can be aware of the limitations and work with them. For example:

I will look into extension samples and may experiment with this.

jrieken commented 7 years ago

fyi - I have uploaded to full sample to https://github.com/Microsoft/vscode-extension-samples/tree/master/smart-template-strings-sample

plievone commented 7 years ago

@jrieken Thanks for extension sample -- I made a quick proof-of-concept of template editing here: https://marketplace.visualstudio.com/items?itemName=plievone.vscode-template-literal-editor

It would be much better with private apis (such as wouldn't nag about saving, and would save the original instead), but works quite ok already. If opening and closing the editors could be handled via public apis, then it would be much quicker to use, but it shows promise for this kind of "language modality" approach, don't you think?

jrieken commented 7 years ago

Sorry, I think my sample wasn't clear enough. The idea not to create an editor with part of the string but to wire up the language smart onto the string. See, language services like our html-support aren't implement against an editor. That means you can use the smartness just like that, without detouring through an editor.

I have updated the sample such that it completes with html with html-tagged strings. See https://github.com/Microsoft/vscode-extension-samples/commit/b7043bdc241b17ad020ad7fb86c62f5fb3dc688b

screen shot 2017-03-24 at 16 34 53

plievone commented 7 years ago

@jrieken Thanks, I will experiment with this a bit further. But also one person just gave the extension five stars as giving autocompletion for their CSS in styled-jsx template literals, and those kind of things come easily with this kind of generic solution... Decorations, other language specific helpers and extensions what people might use, work also as they are just documents, etc. Just tried it with pgsql extension, works great. If you think the UX similar to diff side-by-side view, or perhaps dimmed/highlighted layers on top of each other, you may see it? I like that the context switch between different languages becomes more physical, it is already a bit much to interpret nested string templates with conditionals, and if both languages are highlighted at the same time it becomes more difficult to read... But let's see, of course that could be quickly configurable with a inline autocompletion too.

It would be wonderful if you would implement something like this with internal APIs, it would be a hit. In the meantime I might post the extension link to other threads so people have this option too, even if the UX leaves much to be improved at this point.

plievone commented 7 years ago

@jrieken Template Literal Editor for VS Code has now over 500 downloads, it seems to work nicely. Thanks for your guidance! Also pinging @aeschli and @alexandrudima, in case you are interested. There's really basic support for heredocs and literal strings in other languages too via regexes. You can find the source here: https://github.com/plievone/vscode-template-literal-editor/blob/master/src/extension.ts . I like that Ctrl+Enter is all it needs, very easy to use.