Is it possible to jump to an existing symbol in file from Extension?
Something of the sort:
goToSymbol(symbol: string)
P.S. I don't want to provide a DocumentSymbolProvider. How to use existing ones?
async function getSymbols(document: TextDocument): Promise<DocumentSymbol[]> {
return await commands.executeCommand<DocumentSymbol[]>('vscode.executeDocumentSymbolProvider', document.uri) || [];
}
async function goToSymbol(document: TextDocument, symbolName: string) {
const symbols = await getSymbols(document);
const findSymbol = symbols.find(symbol => symbol.name === symbolName);
const activeTextEditor = window.activeTextEditor;
if (findSymbol && activeTextEditor) {
activeTextEditor.revealRange(findSymbol.range, vscode.TextEditorRevealType.AtTop);
activeTextEditor.selection = new Selection(findSymbol.range.start, findSymbol.range.start);
}
}
Note: the code above should be enough to go to symbol of 1 lvl.
Nested symbols accessible as .children (on every element of symbols)
Related
public var O_RDONLY: Int32 { get }
When I'm looking at stuff inside Darwin.sys.* or Darwin.POSIX.* for example, a lot of these constants are defined as getters. But how does one see the actual value without evaluating the code?
public var O_RDONLY: Int32 { get }
is what the Swift importer generates from the macro definition
#define O_RDONLY 0x0000 /* open for reading only */
in the <sys/fcntl.h> include file. Although this is a fixed value, known at compile time, the Swift importer does not show the value in the generated Swift interface.
Note also that a macro definition in a C header file may depend on other macros, and on other “variables” such as compiler flags, the processor architecture, etc.
I am not aware of a way to navigate to that C definition from a Swift file, or any other way to show the defined value in a pure Swift project. As a workaround, one can
add a C file to the project,
use the macro in some C function, and
“jump to definition” from there.
I ended up with the following solution:
const fs = require('fs');
const { exec } = require("child_process");
const getterRegEx = /^(.*)public var (.+): (.+) { get }(.*)$/;
const code = String(fs.readFileSync('./generatedSwift.swift'));
const lines = code.split('\n').map((line, i) => {
const arr = getterRegEx.exec(line);
if (arr) {
const [all, prefix, constant, type, suffix] = arr;
return `print("let ${constant}: ${type} = ", ${constant}, separator: "")`;
}
return `print("""\n${line}\n""")`;
});
lines.unshift('import Foundation');
fs.writeFileSync('./regeneratedSwift.swift', lines.join('\n'));
exec('swift ./regeneratedSwift.swift', (err, stdout, stderr) => {
if (err) {
console.error(`exec error: ${err}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
Copy definitions generated by the XCode and save into a file named generatedSwift.swift the run node index.js in the same folder.
The output will contain the Swift code where all
public var Constant: Type { get }
are replaced with
let Constant = Value
and all other lines will remain the same.
I'm working on implementing this the File System provider functionality:
https://code.visualstudio.com/api/references/vscode-api#FileSystemProvider
I'd like the following writeFile function to automatically create any necessary parent directories as needed. Was wondering if any one else has done anything similar, or how the experts would approach it.
https://github.com/microsoft/vscode-extension-samples/blob/aa42c2ec7248059ff5903ff11aa6eeabc04329bf/fsprovider-sample/src/fileSystemProvider.ts#L80
Option 1:
change the following line to silent=true and if it returns an undefined, create the necessary directory
https://github.com/microsoft/vscode-extension-samples/blob/aa42c2ec7248059ff5903ff11aa6eeabc04329bf/fsprovider-sample/src/fileSystemProvider.ts#L199
option 2:
Extend the logic of this if statement or replace the following error with some logic to create the folders
https://github.com/microsoft/vscode-extension-samples/blob/aa42c2ec7248059ff5903ff11aa6eeabc04329bf/fsprovider-sample/src/fileSystemProvider.ts#L183
thoughts or suggestions?
I was able to get this working with the following function. I tried the follow the existing code style and approach for simplicity.
/**
* make parent directires if not present
*/
private _makePath(uri: Uri) {
// get the base directory
const dirname = uri.with({ path: path.posix.dirname(uri.path) });
// split the base directory to the folder parts
const parts = dirname.path.split('/');
// array to hold the parent folders as we create them
const fullPath: string[] = [];
// loop through folders
for ( const part of parts) {
if (!part) {
continue; // empty part, continue with loop
}
// track the folder path as we check/create them
fullPath.push(part);
// see if current folder exists
const here = this._lookup(Uri.parse(...fullPath), true);
if (!here) {
// current folder not found, so create it
this.createDirectory(Uri.parse(path.join(...fullPath)));
} else {
console.log('directory exists already: ', part);
}
}
}
Then call the function at each file creation:
writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }, id?: string): void {
console.log('writefile: ', uri);
const basename = path.posix.basename(uri.path);
// *** create dir path if not there ***
this._makePath(uri);
const parent = this._lookupParentDirectory(uri);
let entry = parent.entries.get(basename);
if (entry instanceof Directory) {
throw vscode.FileSystemError.FileIsADirectory(uri);
}
if (!entry && !options.create) {
throw vscode.FileSystemError.FileNotFound(uri);
}
if (entry && options.create && !options.overwrite) {
throw vscode.FileSystemError.FileExists(uri);
}
if (!entry) {
entry = new File(basename, id);
parent.entries.set(basename, entry);
this._fireSoon({ type: vscode.FileChangeType.Created, uri });
}
entry.mtime = Date.now();
entry.size = content.byteLength;
entry.data = content;
this._fireSoon({ type: vscode.FileChangeType.Changed, uri });
}
How can I tell my extension to look first for &s_?
One solution would be to rename all variable to capital prefix S_, but this is not an option in my scenario.
I am working on an extension for Visual Studio Code(VSC). Variable are defined as followed:
&i_ Var_Six`
&s_ SampelTwo
&...
The implementation in VSC is done by "direct implementation" like that:
let provider1 = vscode.languages.registerCompletionItemProvider({ language: 'test' }, {
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
vscode.languages.registerCompletionItemProvider
var completions = new Array();
for (let knownElement of TestVar) {
completions.push(new vscode.CompletionItem(knownElement));
}
return completions;
}
});
context.subscriptions.push(provider1);
Now the problem I have is, that the autocompleting looks first for capital letters but also recognizes the & not as part of the variable.
What I want is that I can start typing "s_" and it would suggest all known variables starting with "&s_".
What VSC actually is suggesting for input “s_” is "i_Var_Six". This is because it looks for the capital letter “S” (camelcase) and the char “ _ ” it does not recognize the char “&” because it is defined as a “word separator”.
i'm not sure why & doesn't work, this is the closest way of implementing it, that i know of.
(read the comments)
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
const TestVar: string[] = ['&_awd', '&_Awd', '&_213', '$_34', '$_23'];
let provider1 = vscode.languages.registerCompletionItemProvider(
{ language: 'plaintext' },
{
provideCompletionItems(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken,
context: vscode.CompletionContext
) {
var completions = [];
for (let knownElement of TestVar) {
const item = new vscode.CompletionItem(knownElement);
// i added this so that the first & sign gets removed,
// you might want to add a check so that this edit only gets added when the user typed a & sign.
item.additionalTextEdits = [
{ newText: '', range: new vscode.Range(new vscode.Position(position.line, position.character - 1), position), },
];
completions.push(item);
}
return completions;
},
},
'.',
'&' // add this so that the completions get triggered when the user types the & sign.
);
context.subscriptions.push(provider1);
}
I am developing an extension which requires me to get the locations of the variable declarations.
For example,
var x = 5;
console.log(x);
Does the VS Code API provide functionality like getVariableLocations() which will return the position of the var x = 5;?
You can get the document symbols by running 'vscode.executeDocumentSymbolProvider'.
Here's an example that executes the command on the active document, and then converts the nested list of symbols (each DocumentSymbol can have children) into a flat list filtered by SymbolKind.Variable:
function findVars(symbols: vscode.DocumentSymbol[]): vscode.DocumentSymbol[] {
var vars =
symbols.filter(symbol => symbol.kind === vscode.SymbolKind.Variable);
return vars.concat(symbols.map(symbol => findVars(symbol.children))
.reduce((a, b) => a.concat(b), []));
}
var activeEditor = vscode.window.activeTextEditor;
if (activeEditor !== undefined) {
vscode.commands
.executeCommand<vscode.DocumentSymbol[]>(
'vscode.executeDocumentSymbolProvider', activeEditor.document.uri)
.then(symbols => {
if (symbols !== undefined) {
for (const variable of findVars(symbols)) {
console.log(variable.name);
}
}
});
}
When running this on this code snippet itself, it logs activeEditor, vars and variable. You can check the position with DocumentSymbol.range.
I am creating a vscode extension that does some custom auto-completing of files paths.
I want to take what the user has typed, and if that value resolves to a folder in the workspace, I want to list all the files in that folder for auto-complete.
For example, given:
a workspace located at: /home/me/my-vs-project
with files:
/home/me/my-vs-project/assets/dog.png
/home/me/my-vs-project/assets/cat.jpeg
If I type in 'assets' or './assets' into vscode, the extension should be able to provide me an autocomplete list of:
'./assets/dog.png'
'./assets/cat.png'
Here's a snippet of the code that doesn't work (returns 0 results)..
let inputAsWorkspaceRelativeFolder = getInput(document, position); // for example, would return: '/home/me/my-vs-project/assets' for input of './assets'
let glob = inputAsWorkspaceRelativeFolder + '/*';
vscode.workspace.findFiles(glob, null, 100).then((uris: vscode.Uri[] ) => {
uris.forEach((uri: vscode.Uri) => {
console.log(uri);
});
});
For some reason, the above code is returning 0 uris though. Thoughts on how I have to format the glob to make this happen? and/or if there is a better approach?
I was able to do this using vscode.RelativePattern -- I'm sure I could've done it using generic GlobPatterns but im still not clear what the findFiles(..) consider the 'root' when matching files; RelativePattern is explicitly relative to the workspace root.
let workspaceFolder: vscode.WorkspaceFolder | undefined = vscode.workspace.getWorkspaceFolder(document.uri);
if (!workspaceFolder || document.isUntitled) {
return undefined;
}
// Workspace folder: /home/me/my-project
let workspaceFolderPath: string = workspaceFolder.uri.path;
let relativeSearchFolderPrefix = path.normalize(path.dirname(document.uri.path) + '/' + searchText);
relativeSearchFolderPrefix = path.relative(workspaceFolderPath, relativeSearchFolderPrefix);
let relativePattern: vscode.RelativePattern = new vscode.RelativePattern(
workspaceFolderPath,
relativeSearchFolderPrefix + '/**/*.{png,jpeg,jpg,gif}');
return vscode.workspace.findFiles(globPattern, null, 50).then((uris: vscode.Uri[] ) => {
let relativePaths: string[] = [];
uris.forEach((uri: vscode.Uri) => {
relativePaths.push(path.relative(current, uri.path));
});
// trivial custom function that turns an array of strings into CompletionItems
return getCompletionItems(relativePaths, vscode.CompletionItemKind.File);
});
😊👋🏻
I think you wronged the glob.
I found this intresting wiki about Glob pattern composition.
let inputAsWorkspaceRelativeFolder = 'asset'; // for example, would return: '/home/me/my-vs-project/assets' for input of './assets'
//https://github.com/ev3dev/vscode-ev3dev-browser/wiki/Glob-Patterns
let glob = '**/'+inputAsWorkspaceRelativeFolder+'/*.*';//or +'/{*.png,*.jpeg}';
Or you can use the node built-in fs
import * as fs from 'fs';
fs.readdir(inputAsWorkspaceRelativeFolder, (err, files: string[]) => {
files.forEach((file: path) => {
const uri = vscode.Uri.file(file);
console.log(uri);
});
});
More simple, if you want to get all the files in the asset folder and don't want to filter for extension.