Set language of a text document in a VSCode extension - visual-studio-code

I have a Visual Studio Code extension where I try to open a
virtual editor:
vscode.workspace.openTextDocument(vscode.Uri.parse(previewSchema + ":" + path))
context.subscriptions.push(extractHibernateLogCommand, vscode.Disposable.from(
vscode.workspace.registerTextDocumentContentProvider(previewSchema, hibernateExtractorProvider)
));
Those documents are always language:plain-text. Is it possible to change this programmatically to "SQL" to have the correct highlighting?
Full code

Since VSCode 1.28 (September 2018), it's also possible to set the language mode for a document after it has been created using languages.setTextDocumentLanguage():
Set (and change) the language that is associated with the given document.
Note that calling this function will trigger the onDidCloseTextDocument event followed by the onDidOpenTextDocument event.
Here's a simple example that opens a document containing {} and sets the language to JSON:
vscode.workspace.openTextDocument({content: "{}"}).then(document => {
vscode.window.showTextDocument(document);
vscode.languages.setTextDocumentLanguage(document, "json");
});

I found a solution by myself:
let options: Object = {
content: string,
language: "sql"
};
vscode.workspace.openTextDocument(options).then(doc => {
vscode.window.showTextDocument(doc, vscode.ViewColumn.One);
}, err => {
vscode.window.showErrorMessage(err);
});
A solution when using TextDocumentContentProvider seems not to be possible.
The commit with my change

Open the Command Palette (View->Command Palette)
run "Configure language specific settings"
In the Select Language drop down there should be a setting for SQL

Related

How to trigger activation of the vscode markdown extension

In my VS Code extension I have some code that uses the built in Markdown extension. I capture a reference to it by registering as a markdown plugin and putting the following code at the end of my extension's activate method.
return {
extendMarkdownIt(mdparam: any) {
return md = mdparam;
}
};
Markdown calls this when it activates.
Generally this is not a problem. Most of the use cases for my extension involve a markdown file already loaded into the active editor, and the loading of this file triggers activation of the markdown extension.
However there are some legitimate use cases in which this is not so.
I need to programmatically trigger activation of the markdown extension. Some of these cases involve having a different kind of file open in the active editor so loading a markdown file into it is not an acceptable option.
Some potential strategies:
Change the language mode. There is a command workbench.action.editor.changeLanguageMode but no documentation. I tried
vscode.commands.executeCommand('workbench.action.editor.changeLanguageMode', 'md');
but this triggers the UI
so I tried a pattern I've seen in the parameters of other commands and added , true. This suppressed the UI but doesn't seem to work.
Load a markdown file into a new editor then close it again. This should work, but it's ugly.
Put something in the contributions section of my extension that changes the activation trigger for the markdown extension so that it is triggered by the other file types on which my extension operates.
Of these options my favourite would be 3 but I don't even know whether this is even possible. Option 1 is hampered by the crappy (in many cases non-existent) documentation for vscode internal commands.
Option 1 it is. If anyone knows how to do option 3 please tell, the solution below is a ghastly hack.
It is possible to trigger activation of the Markdown extension by changing the document language of any open editor to markdown. In the event that there are no open editors a document with the markdown language set can be created in memory and loaded into an editor.
If VS Code is busy loading extensions activation can take several hundred milliseconds so the best thing to do is watch the variable into which markdown-it is captured.
The variable md is a global (global to my extension, not the whole of VS Code) into which a reference is acquired as shown in the question.
let ed = vscode.window.activeTextEditor;
if (ed) {
let lid = ed.document.languageId;
if (lid !== "markdown") {
vscode.languages.setTextDocumentLanguage(ed.document, "markdown").then(
function waitForMd() {
if (md) {
vscode.languages.setTextDocumentLanguage(ed!.document, lid);
} else {
setTimeout(waitForMd, 100);
}
}
);
}
} else {
vscode.workspace.openTextDocument({ language: "markdown" }).then(doc => {
vscode.window.showTextDocument(doc).then(
function waitForMd() {
if (md) {
vscode.commands.executeCommand("workbench.action.closeActiveEditor");
} else {
setTimeout(waitForMd, 100);
}
});
});
}
Once the capture completes we can restore the true language or close the editor as appropriate. To be realistic the second case (no active editor) is unlikely because my own extension won't activate until you load something. At any rate it works stably now. The larger project is progressing nicely.

VSCode extension: disable repeating things in code completion

I am creating a language extension for VSCode using Java and the LSP4J library. It is something like this.
But I have a problem - if the user presses Ctrl+Space, and the language server returns an empty list, VSCode will still offers its options - things that are already in the code. How can I get it to display something like "No suggestions" instead?
The text-based completion you're seeing there can be disabled with the "editor.wordBasedSuggestions" setting.
Extensions can change the default value of a setting for a particular language by contributing configurationDefaults in package.json:
"contributes": {
"configurationDefaults": {
"[lang]": {
"editor.wordBasedSuggestions": false
}
}
}
Where lang is the ID of the language in question.
If the language server sends back an empty list you could add an artificial entry with text: "No suggestions" to the completion list.

VSCode autocomplete/intellisense for strings

While working with a PHP or JavaScript file in sublime, if I type the following code:
$test = "Scott";
If($test == null) {
$test2 = "Scott"
}
When I typed in “$test2 = ‘Sc...” Sublime would autocomplete “Scott” since it was a word it found as a string in the current document scope.
However, when I do the same test in VSCode, it doesn’t pick it up or offer any suggestions. Is this possible in VSCode? I have already turned on the quicksuggestions to all true in the preferences. I am not sure what else I could do here or if there is an additional plugin that I need to download. Thanks!
Like this?
My settings:
intelephense extension enabled
{
"editor.quickSuggestions": {
"other": true,
"comments": false,
"strings": true
},
"editor.wordBasedSuggestions": true,
"php.suggest.basic": false
}
These are word based suggestions. They are controlled by the editor.wordBasedSuggestions setting.
However, word base suggestions will only show up when no valid language based suggestions are found (see https://github.com/Microsoft/vscode/issues/21611). JS should do the right thing inside strings here:
But the built-in php language support will still return language based suggestions inside strings, which is why the word based suggestions do not show up

How to enable "Go to symbol" with a custom language in vscode?

I have made a custom language extension and I would like to enable the "Go To Symbol" feature. I have tried to follow the guidelines here, but I'm still kind of lost.
I think all I need to do is implement a DocumentSymbolProvider, but I'm not really sure how to go about it.
UPDATE
The example language server docs point to a repo that is deprecated. It is replaced with this one, which is much more complex. I think the simplest example I can find there is the lsp-sample, but it doesn't use a DocumentSymbolProvider.
I've found other repos that do use symbol providers, but they are a bit overwhelming. I can't figure out what needs to go where (for example, do I need both a client and a server? Some packages only seem to use an extension.ts without both client and server folders).
All I really want to do in my language is detect lines that start with # and show them in the Go to Symbol pane. I'd love to see a simple tutorial of this.
Do I need both a client and a server?
Not necessarily. As the language support docs you linked show, there's usually two options: a "direct implementation" and one using the Language Server Protocol. For the former, you don't need a client / server architecture. Language Servers have the advantage of being editor-agnostic, you can theoretically use them in any editor that implements the protocol. A direct implementation is limited to usage in VSCode.
All I really want to do in my language is detect lines that start with # and show them in the Go to Symbol pane.
Here's the extension.ts for a very simple example of a "direct implementation" for this:
'use strict';
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(
{language: "foo"}, new FooDocumentSymbolProvider()
));
}
class FooDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
public provideDocumentSymbols(document: vscode.TextDocument,
token: vscode.CancellationToken): Thenable<vscode.SymbolInformation[]> {
return new Promise((resolve, reject) => {
var symbols = [];
for (var i = 0; i < document.lineCount; i++) {
var line = document.lineAt(i);
if (line.text.startsWith("#")) {
symbols.push({
name: line.text.substr(1),
kind: vscode.SymbolKind.Field,
location: new vscode.Location(document.uri, line.range)
})
}
}
resolve(symbols);
});
}
}
You will also need to add this to the package.json generated by the VSCode extension template. It registers the foo language and activates the extension when a file with the .foo extension is opened:
"activationEvents": [
"onLanguage:foo"
],
"contributes": {
"languages": [{
"id": "foo",
"extensions": [".foo"]
}]
}
Here it is in action:

VSCode create unsaved file and add content

In VS Code I would like to create a new document in a new editor (same window), but it need to remain unsaved. I cannot find a way to programmatically set the content of this document while it is in a unsaved state.
I have used:
commands.executeCommand("workbench.action.files.newUntitledFile")
but there seems to be no way to then add content to the file.
When I create a new temporary file and open it with:
workspace.openTextDocument(path)
The file is already saved.
Any thoughts?
Try using openTextDocument with an untitled document to create a unsaved file at a given path, and then use WorkspaceEdit to add some text:
import * as vscode from 'vscode';
import * as path from 'path';
const newFile = vscode.Uri.parse('untitled:' + path.join(vscode.workspace.rootPath, 'safsa.txt'));
vscode.workspace.openTextDocument(newFile).then(document => {
const edit = new vscode.WorkspaceEdit();
edit.insert(newFile, new vscode.Position(0, 0), "Hello world!");
return vscode.workspace.applyEdit(edit).then(success => {
if (success) {
vscode.window.showTextDocument(document);
} else {
vscode.window.showInformationMessage('Error!');
}
});
});
The new file will be unsaved when first opened, but saved to the given path when a user saves it.
Hope that provides a good starting point.
I don't know how to open it in the editor, but you can create an unsaved file with content like the following:
vscode.workspace.openTextDocument({
content: "your content",
language: "text"
});
That should be supported in VSCode 1.54 (Feb. 2021), meaning the vscode.workspace.openTextDocument script from Matt is implemented by default:
Open Editors New Untitled File action
We have introduced a New Untitled File action in the Open Editors view title area.
I was having some issues when the document was eventually saved using this solution from Matt, but I was able to use it in combination with DarkTrick's response.
By using the default behavior of creating an empty document and making it active in the then clause.
vscode.workspace.openTextDocument({
content: newXmlContent,
language: "xml"
}).then(newDocument => {
vscode.window.showTextDocument(newDocument);
});
This allows me to create an untitled document with any content I want and show it in the editor. I was not able to give it a specific name though. This might be a limitation of creating an untitled document.