Creating IntelliSense based on compiled JavaScript for other language? - vscode-extensions

I thought about creating an extension that offers IntelliSense (i.e. code completion) for CoffeeScript, a popular language that compiles to JavaScript. By IntelliSense I primarly mean autocompletion: Properties, function parameters, automatic imports. If possible, also Go-To-Definitions. Despite its huge success and age, nothing like it exists in any IDE for CS, as far as I know. This is arguably because integrating the language server, import scanning, function signature analysis etc. are complex and this is not an easy task per se.
However, CoffeeScript natively and efficiently compiles into JavaScript, and JS Intellisense in VSCode is splendid. For a very basic yet mostly functional extension, the following approach sounds feasible:
Register completion item provider for type CoffeeScript files
When providing a completion item, take the compiled JavaScript code, get profuse IntelliSense information from the JS/TS language server (how? do I need my own instance? I don't think VSC even uses an actual JS LSP internally?) and show it in the CoffeeScript file cursor position
It does not sound difficult, but I don't know how to proceed. Here is a pseudo code snippet of how I imagine it to work:
class Provider {
provideCompletionItems(document, position, token) {
const word = document.getText(document.getWordRangeAtPosition(position))
const text = document.getText().split('\n')
const currentLineno = position.line
const textWithoutCurrentLine = text.filter((line, lineno) => lineno != currentLineno)
const compiled = CoffeeScript.compile(textWithoutCurrentLine.join('\n'), { sourceMap: true })
const currentLinenoJS = compiled.sourceMap[currentLine].lineno // don't know the syntax yet
const compiledWithWord = compiled.js.split('\n').splice(currentLinenoJS, 0, word)
// These methods don't exist, what do I do?
const JSProvider = vscode.languages.simulateCompletionItemProvider('javascript')
const completionItems: CompletionItem[] = JSProvider.getCompletionItemsForTextAndLine(textWithoutCurrentLine, currentLinenoJS)
return completionItems
}
}
vscode.languages.registerCompletionItemProvider('coffeescript', Provider));
This might be more confusing than helpful, but it's what the code could look like. I would love a hint on how to access the JavaScript intellisense features for dynamically generated code, from within the completion item provider handler of a CoffeeScript file. If this is doable, it would allow for simple extensions for similar compile-to-JS languages too.
In other words: JS has all these amazing autocompletion features and CS compiles to JS, including source maps. How can I leverage this to get autocompletion for CS?

Register completion item provider for type CoffeeScript files
This would be following programmatic language features. For a middleware kind of extension there are two main architectures possible, outlined here: Language services or request forwarding.
I eventually went with implementing a language service extension, outlined here.
This approach was significantly easier than writing a native analyzer.
When providing a completion item, take the compiled JavaScript code, get profuse IntelliSense information from the JS/TS language server (how? do I need my own instance?
Yes
I don't think VSC even uses an actual JS LSP internally?)
Yes, tsserver does not implement LSP, but offers its own APIs, but there are other usable implementations. So you need to interact with either of those.

Related

How to properly test component logic in Vue.js

I'm writing a new Vue project and want to test my components with vue-test-utils.
So far I've been writing unit-tests with a test case for each component method, testing its expected behavior while mocking other methods that it might call.
Recently I've read on the Vue docs that the unit tests for components should test general behavior and not rely on implemenation details:
https://vuejs.org/guide/scaling-up/testing.html#component-testing
which makes sense, but I still want to test the logic of my component's methods.
I don't want to extract it to different files / composables because they heavily rely on the component's data and other methods, and I don't wanna pass everything as parameters.
What do you recommend regarding this?
I can't mock methods anymore since setMethods is now deprecated in vue-test-utils, which is making it harder to test each method separately.
do you think I should give up on testing each method?
I find it helpful to test methods because usually small changes make those tests fail which help me notice errors in the code, but also makes it harder because every small legitimate change requires unit-test changes as well.
We have done unit test using vue-jest. Here are the steps.
Use the following code to set up the crypto property globally. This will allow Jest to access window.crypto and won't cause any issue.Create a crypto.mock file and paste following code.
const crypto = require('crypto')
Object.defineProperty(global.self, 'crypto', {
value: {
getRandomValues: arr => crypto.randomBytes(arr.length)
}
})
Now import this file in your test file component. Now You can use jasmine.Spy to spy on the elements.
let spyFor: jasmine.Spy; spyFor = spyOn(function.hello, 'hello'); expect(spyFor).toHaveBeenCalledTimes(1);
In this way you can mock on the methods. Pls free to ask if you have anything more.

How to use Web Speech API in Kotlin Multiplatform for web application

Do you know how to use Web Speech API in KMM project for Web application: https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API/Using_the_Web_Speech_API
I'm using Kotlin to build the web app, and the web app require speech to text feature.
I'm not familiar with this particular WEB API, but here's the general process of wrapping global JS APIs in Kotlin so hopefully you'll be able to correct the odd inconsistencies yourself via trial and error.
Firstly, since the target API is global, there's no need for any meta-information for the compiler about where to source JS code from - it's present in the global context. Therefore, we only need to declare the shape of that global context. Normally that would be a straightforward task as outlined in this article, however there's a caveat here which requires some trickery to make it work on all the browsers:
As mentioned earlier, Chrome currently supports speech recognition with prefixed properties, therefore at the start of our code we include these lines to feed the right objects to Chrome, and any future implementations that might support the features without a prefix:
var SpeechRecognition = window.SpeechRecognition || webkitSpeechRecognition;
var SpeechGrammarList = window.SpeechGrammarList || webkitSpeechGrammarList;
var SpeechRecognitionEvent = window.SpeechRecognitionEvent || >webkitSpeechRecognitionEvent;
But let's ignore that for now since the API shape is consistent across the implementation, and name is the only difference that we'll address later. Two main API entities we need to wrap here are SpeechRecognition and SpeechGrammarList, both being classes. However, to make it easier to bridge the inconsistent names for them later on, in Kotlin it's best to describe their shapes as external interfaces. The process for both is the same, so I'll just outline it for SpeechRecognition.
First, the interface declaration. Here we can already make use from EventTarget declaration in Kotlin/JS stdlib. Note that the name of it does not matter here and will not clash with webkitSpeechRecognition when present since we declare it as an interface and as such we only care about the API shape.
external interface SpeechRecognition: EventTarget {
val grammars: SpeechGrammarList // or dynamic if you don't want to declare nested types
var lang: String
// etc...
}
Once we have the API shape declared, we need to bridge naming inconsistencies and provide a unified way to construct its instances from Kotlin. For that, we'll inject some hacky Kotlin code to act as our constructors.
// We match the function name to the type name here so that from Kotlin consumer's perspective it's indistinguishable from an actual constructor.
fun SpeechRecognition(): SpeechRecognition {
// Using some direct JS code to get an appropriate class reference
val cls = js("window.SpeechRecognition || webkitSpeechRecognition")
// Using the class reference to construct an instance of it and then tell the kotlin compiler to assume it's type
return js("new cls()").unsafeCast<SpeechRecognition>()
}
Hopefully this gives you the general idea of how things tie together. Let me know if something's still not quite clear.

Namespace and module confusion in typescript?

The official site of Typescript get me ask a question,
"Do we need to use namespace or not?".
The following quote explains the 2 things well:
It’s important to note that in TypeScript 1.5, the nomenclature has
changed. “Internal modules” are now “namespaces”. “External modules”
are now simply “modules”, as to align with ECMAScript 2015’s
terminology, (namely that module X { is equivalent to the
now-preferred namespace X {).
So, they suggest that TS team prefer namespace.
Further, it says we should use "namespace" to struct the internal module:
This post outlines the various ways to organize your code using
namespaces (previously “internal modules”) in TypeScript. As we
alluded in our note about terminology, “internal modules” are now
referred to as “namespaces”. Additionally, anywhere the module keyword
was used when declaring an internal module, the namespace keyword can
and should be used instead. This avoids confusing new users by
overloading them with similarly named terms.
The above quote is all from the Namespace section, and yes, it says again, but in a internal secnario.
but in the module section, one paragraph, says that:
Starting with ECMAScript 2015, modules are native part of the
language, and should be supported by all compliant engine
implementations. Thus, for new projects modules would be the
recommended code organization mechanism.
Does it mean that I don't need to bother with namespace, use module all along is the suggested way to develop?
Does it mean that I don't need to bother with namespace, use module all along is the suggested way to develop?
I wouldn't put it exactly that way... here's another paraphrase of what has happened. One upon a time, there were two terms used in Typescript
"external modules" - this was the TS analog to what the JS community called AMD (e.g. RequireJS) or CommonJS (e.g. NodeJS) modules. This was optional, for some people who write browser-based code only, they don't always bother with this, especially if they use globals to communicate across files.
"internal modules" - this is a hierarchical way of organising your variables/functions so that not everything is global. The same pattern exists in JS, it's when people organise their variables into objects/nested objects rather than having them all global.
Along came Ecmascript 2015 (a.k.a. ES6), which added a new formal, standard format that belonged in the "external modules" category. Because of this change, Typescript wanted to change the terminology to match the new Javascript standard (being that it likes to be a superset of Javascript, and tries its best to avoid confusion for users coming from Javascript). Thus, the switch of "external modules" being simplified to just "modules", and "internal modules" being renamed to "namespaces".
The quote you found here:
Starting with ECMAScript 2015, modules are native part of the language, and should be supported by all compliant engine implementations. Thus, for new projects modules would be the recommended code organization mechanism.
Is likely alluding to guidance for users who were not yet using (external) modules. To at least consider using it now. However, support for ES6 modules is still incomplete in that browsers as of May 2016 don't have built-in module loaders. So, you either have to add a polyfill (which handles it at runtime) like RequireJS or SystemJS, or a bundler (like browserify or webpack) that handles it at build time (before you deploy to your website).
So, would you ever use both modules (formerly "external modules") and namespaces? Absolutely - I use them both frequently in my codebases. I use (external) modules to organise my code files.
Namespaces in Typescript are extremely useful. Specifically, I use namespace declaration merging as a typesafe way to add extra properties to function objects themselves (a pattern often used in JS). In addition, while namespaces are a lot like regular object variables, you can hang subtypes (nested interfaces, classes, enums, etc.) off of their names.
Here is an example of a function with a property (very common in NodeJS libs):
function someUsefulFunction() {
// asynchronous version
return ...; // some promise
}
namespace someUsefulFunction {
export function sync() {
// synchronous version
}
}
This allows for consumers to do this common NodeJS pattern:
// asynchronous consumer
someUsefulFunction()
.then(() => {
// ...
});
// synchronous consumer
someUsefulFunction.sync();
Similarly, say you have an API that takes in an options object. If that options type is specific to that API,
function myFunc(options?: myFunc.Options) {
// ...
}
namespace myFunc {
export interface Options {
opt1?: number;
opt2?: boolean;
opt3?: string;
}
}
In that case, you don't have to pollute a larger namespace (say whole module scope) with the type declaration for the options.
Hope this helps!

module controller with initiation parameter

When using m.module, I often would like to provide arguments to the controller constructor so that the first rendering starts with the right data. However, the Mithril documentation and examples always show module.controller() and module.vm.init() without parameters.
To go around this issue and have module.controller(initData) I've resorted to use this small utility function to wrap and extend the existing m.Module:
var mModule = function (dom, mod, arg) {
return m.module(dom, {
view: mod.view,
controller: mod.controller.bind(mod.controller,arg)
});
};
Questions:
Is this an anti-pattern? Is there an alternate recommended way instantiate the module with custom external data?
Would this cause issues with m.route? I saw some mentions of recursive calls in the source code but could not get my head around it.
Following the 2 points above, is the lack of parameter for m.module a deliberate design choice?
Oh...and thanks to all involved for the existing documentation and discussions.
No, it's not an anti-pattern, and it's an idea that is explored in one of the blog articles, and also by Moria (a router extension library for Mithril)

Any way to get JsDoc help in IntelliJ when documenting AMD modules?

I like using JsDoc, as it gives me a lot of help when using WebStorm (or other IntelliJ derivates), such as parameter lookup, quick access to documentation, etc.
Most examples assume that you can write something like
/** #class MyClass */
var MyClass = function() {
}
But if you wrap your code in anonymous functions to avoid polluting the namespace (say, due to concatenating the js files), then WebStorm no longer finds the documentation, and I no longer get much help.
Is there any way around this?