-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
[tsserver] custom command (ex: to collect Angular2 @Component/selector) #5730
Comments
//cc:@billti for your other question about extracting decorator information, here is a sample: function visit(sourceFile: ts.SourceFile) {
visitNode(sourceFile);
function visitNode(node: ts.Node) {
if (node.kind === ts.SyntaxKind.ClassDeclaration) {
// only look in classes
if (node.decorators) {
node.decorators.forEach(decorator => {
if (decorator.expression.kind === ts.SyntaxKind.CallExpression) {
// decorators of the form doc({...})
let callExpression = <ts.CallExpression>decorator.expression;
if (callExpression.expression.getFullText() === "Component") {
// found a component
if (callExpression.arguments && callExpression.arguments.length >= 1) {
let firstArg = callExpression.arguments[0];
if (firstArg.kind === ts.SyntaxKind.ObjectLiteralExpression) {
let object = <ts.ObjectLiteralExpression>firstArg;
object.properties.forEach(p => {
if (p.kind === ts.SyntaxKind.PropertyAssignment) {
// key is selector
if ((<ts.PropertyAssignment>p).name.getText() === "selector") {
// found it!
console.log((<ts.PropertyAssignment>p).initializer.getText());
}
}
});
}
}
}
}
});
}
}
ts.forEachChild(node, visitNode);
}
}
// Parse
let sourceFile = ts.createSourceFile("test.ts", `
import {Component} from 'angular2/angular2';
@Component({
selector: 'my-app'
})
class AppComponent { }
`, ts.ScriptTarget.ES6, /*setParentNodes */ true);
visit(sourceFile); |
I actually spent a lot of time on this over the past couple weeks. This turns out to be quite a non-trivial problem, and I've hit a few dead ends and back-tracked. Part of the problem is to do anything meaningful with the template code, you need to synthesize a file with some generated content from the template and ask questions about that, yet the program used by the language service is immutable once created, so you end up needing another language service working with the synthesized program, which the actual language service the editor uses then delegates to. (You still need the original one also to ask some of the initial questions before creating/delegating the synthesized one). I've been playing around with a few different ideas (my scratch pad for various prototypes is at https://github.com/billti/TypeScript/commits/ngml . I use a similar syntactic model to Mohamed above for now to detect components, but note this could be fooled by a similar shape class/decorator that wasn't actually an Angular component), but I don't think we're close to committing to any plugin model and timeline yet. Once you expose something like this for folks to start using, you've committed yourself to supporting it and maintaining compatibility, and this is too complex an area to rush out a design. |
@billti wow, I'm very impressed with your work. If I have understood you provide the capability to add plugin to support completion for template object property completion for ng attributes, for model, etc.
You mean that you have not planned to integrate your work in master TypeScript? Is there any chance that you do that in the future? If it's possible, it should be cool if tsserver could load this plugin dynamicly (without compiling a custom tsserver) My initial idea was to add a new command in tsserver at runtime to provide for instance a command which returns the angular model of a project (modules, directives) to display it in an outline like I have done for Angular1 https://github.com/angelozerr/angularjs-eclipse/wiki/Angular-Explorer-View When angular model changes, teh command fires an event with the new change in order to the outline refresh as soon as model changes. This command is added at runtime (no need to compile a custom version of tsserver). Your command is a npm module which starts with So you could have :
After you call your tsserver with parameters Thoses command could be hosted too inside |
May I suggest an alternative? Your proposal is about introducing plugin framework to The routing/execution of the requests inside The idea is to have a wrapper script that fires the standard process.nextTick(() => { // <-- look over here
const ioSession = new IOSession(ts.sys, logger);
process.on("uncaughtException", function(err: Error) {
ioSession.logError(err, "unknown");
});
// Start listening
ioSession.listen();
}); Your wrapper script will go:
Now your project is in charge of complexity, not TypeScript. This pay-as-you-go model is important to flexibility and support cost. If Your custom request handler may recycle the instance of |
@angelozerr The goal is to get this into master at some point, we just can't commit as to when exactly that point is right now, as we've just started exploring this space and trying to understand the scope of the work. There are a number of use-cases we need to think through, and ideally prototype, to ensure we make them practical/possible once we release and support this. The goal is that any plug-ins or extensions would be written and compiled separately, referencing a supported API typing file. I'm just compiling my work as part of TypeScript for now as some of my experimentation also requires changes on the TypeScript side. If I understand your requirements correctly, want you want might be something as simply as an event to your plugin whenever some structure in the code changes that you observe (in this case the Angular model), and you don't want to interfere with any of the existing compiler/language service functionality (such as completions, errors, compilation, etc.). Is this right? One challenge with some of the suggestions above is that currently we can't assume that Node is the runtime and host for the language service or compiler. For example in Visual Studio or tsc.exe, there isn't a 'require' global method that has a standard resolution for locating and loading files. This is why we have abstractions for dealing with the host such as exist in https://github.com/Microsoft/TypeScript/blob/master/src/compiler/sys.ts or the @mihailik While being able to monkey-patch the Session object would certainly be powerful, the approach doesn't really lend itself to a supported and strongly typed extensibilty model. |
@billti agree in long term! My point is to leave the hosting logic to the outer application. Host application is best suited to coordinate lifetime, discovery, asynchrony. And Monkey-patching of (I've changed the PR accordingly) |
After another brief discussion in the team, a further concern is about the other end of the "pipe". We can expose additional commands easily enough on the tsserver end, but then currently most of the editor hosts (e.g. VS, VS Code, Sublime, etc.), have no way of sharing their end of that stdin/stdout pipe to send the new commands. The part of the plugin living in the editor host could spin up it's own instance of tsserver to work with, but then that is duplicating a lot of cost (memory, file watchers, etc.) and gets expensive very quickly. Maybe by definition any plugin that exposes additional commands REQUIRES editor host specific work anyway to call them (i.e. Python code for Sublime, JS for VSCode, Java for Eclipse, etc). But it is an additional wrinkle/challenge that'd we'd need to do work for each editor also to have a plugin model if we wanted a general extensibility story there, rather than baking support for certain plugins into the existing editor support. |
You can't own the editor-side story whatever your best intentions. Brackets, vim, emacs, Cloud9 etc. have their own plans, so prescriptive model will create friction and cost, even if there were one good for Python, Sublime and Java. Perhaps the least-prescriptive model today is to let the editor-side drive, and get out of their way? |
I understand. I'm very exciting with this plugien feature. Hope it will be available soon.
Great!
Yes sure.
Yes to provide an Angular2 Outline.
No, I need that too! To be honnest with you, I have already done those 2 features (Angular Outline and custom completion for ng template) for Angular1 by developping a tern plugin for angular1. So I have started to develop a tern plugin for angular2. But as Angular2 is based on TypeScript, I need to customize acorn parser to parse TypeScript to support decorator and additional syntax of ts (it starts working but it's a very big work). So if TypeScript with tsserver is able to provide the same feature than ternjs plugin, I will use tsserver to support Angular2 inside Eclipse. More, if you provide plugin with tsserver, you will able to support completion inside string for a lot of JavaSCript framework. For instance you could support CSS selectors completions, validation etc like have done https://github.com/angelozerr/tern-browser-extension#css-selector This CSS selectors features works too for jQuery $(""). To support that, I mark the AST node $ and document.querySelector as "CSS selectors". I have a tern plugin which use this information to provide completion, navigation, validation, hover. IMHO, I think you should do the same thing. Mark your node as template.
Ok, require.resolve was just a suggestion (tern use that) to load plugin at runtime. But if you provide an another mean to host plugin, that's fine.
|
@billti @mhegazy I know it's diffiicult for you to give me an answer because plugins features is a POC, but this feature doesn't appear in the Roadmap and I tell me when you could (have the intention to) provide this plugin features (in several weeks? months? years?). I would like just know if I continue to integrate tsserver inside Eclipse to support Angular2 or if I develop an Angular2 tern plugin by waiting for your plugin features (I would prefer the first option with tsserver). Many thanks for your answer! |
@billti can comment better on the timeline. i think he can also share his current work if you are interested in trying it out/helping. The plan is to use this work as a proving grounds for an extensiblity model. once we have a clear idea of the work/API commitment involved, we should be able to publish the details of the new extensiblity API. |
Many thanks @mhegazy for your answer! I'm very excited with the work of @billti and very motivated to continue my integration of TypeScript inside Eclipse with https://github.com/angelozerr/typescript.java |
closing this as it is already handled in #6508 |
At first I explain you what I would like to do with tsserver. @dbaeumer I think you could be interested with this issue for VSCode and Angular2 support.
Take the sample https://angular.io/docs/ts/latest/quickstart.html with Angular2 & TypeScript. There is a ts file:
And the HTML:
The HTML contains
<my-app>
which is binded with decorator @Component/selector property. My goal is to provide completion, navigation inside HTML (Eclipse) editor for<my-app>
. In other words, I need to collect the whole @Component/selector node of the project to use it in a HTML editor. I tell me how I could do that:With ternjs it's possible to write a tern plugin which is able to do that (collect @Component/selector) because:
tsserver could do the same thing by searching inside node_modules with a pattern name like
tsserver-xxxx
to provide a custom command. After that any editor will be able to consume it just by installing with `npm installl tsserver-angular2'Hope you will understand my issue.
The text was updated successfully, but these errors were encountered: