How to get the Multi Selected files from the File Explorer in an extension command - visual-studio-code

In package.json you can add a command to the File Explorer context menu.
In File Explorer you can select multiple files but my command only gets the last selected file URI as argument.
Can I get a list of all the selected files in the File Explorer?

You could look at my extension Find and Transform to see how I parse multiple files when an explorer context menu command is triggered. [There is some extra code in there because that command can be triggered by a keybinding or explorer/editor/tab menus so they have to be handled differently.]
let contextMenuCommandFile = vscode.commands.registerCommand('find-and-transform.searchInFile', async (...commandArgs) => {
let args = {};
if (commandArgs?.length === 1 && !(commandArgs[0] instanceof vscode.Uri)) { // if from keybinding
let argsArray = Object.entries(commandArgs[0]).filter(arg => {
return searchCommands.getKeys().includes(arg[0]);
});
Object.assign(args, Object.fromEntries(argsArray));
}
args.filesToInclude = await parseCommands.parseArgs(commandArgs, "file");
args.triggerSearch = true;
searchCommands.useSearchPanel(args);
});
context.subscriptions.push(contextMenuCommandFile);
You might be missing this async (...commandArgs) => { to get all available passed arguments into an array.
...commandArgs will be of length 1 if coming from a keybinding and length 2 if trigggered from the context menu no matter how many files were selected in the Explorer before right-clicking on one and choosing the command.
commandArgs[0] is the single file (i.e., the last file) on which I right-clicked.
commandsArgs[1] is itself an array of all the selected files in the explorer.
I then send that commandArgs array to be parsed (since I just need a comma-separated list of the files selected) to parseCommands.parseArgs() [okay, strangely-named function!).
The operative bit there is:
else if (commandArgs[1][0] instanceof vscode.Uri) { // explorer/context
for (const resource of commandArgs[1]) {
const thisResource = vscode.workspace.asRelativePath(resource.fsPath);
resources += `${ thisResource }, `;
}
resources = resources.substring(0, resources.length - 2); // strip ', ' off end
return resources;
}

Related

Listing files in folder in vscode extension

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.

Take all text files in a folder and combine then into 1

I'm trying to merge all my text files into one file.
The problem I am having is that the file names are based on data previously captured in my app. I don't know how to define my path to where the text files are, maybe. I keep getting a error, but the path to the files are correct.
What am I missing?
string filesread = System.AppDomain.CurrentDomain.BaseDirectory + #"\data\Customers\" + CustComboB.SelectedItem + #"\";
Directory.GetFiles(filesread);
using (var output = File.Create("allfiles.txt"))
{
foreach (var file in new[] { filesread })
{
using (var input = File.OpenRead(file))
{
input.CopyTo(output);
}
}
}
System.Diagnostics.Process.Start("allfiles.txt");
my error:
System.IO.DirectoryNotFoundException
HResult=0x80070003
Message=Could not find a part of the path 'C:\Users\simeo\source\repos\UpMarker\UpMarker\bin\Debug\data\Customers\13Dec2018\'.
I cant post a pic, but let me try and give some more details on my form.
I select a combobox item, this item is a directory. then I have a listbox that displays the files in my directory. I then have a button that executes my desires of combining the files. thanks
I finally got it working.
string path = #"data\Customers\" + CustComboB.SelectedItem;
string topath = #"data\Customers\";
string files = "*.txt";
string[] txtFiles;
txtFiles = Directory.GetFiles(path, files);
using (StreamWriter writer = new StreamWriter(topath + #"\allfiles.txt"))
{
for (int i = 0; i < txtFiles.Length; i++)
{
using (StreamReader reader = File.OpenText(txtFiles[i]))
{
writer.Write(reader.ReadToEnd());
}
}
System.Diagnostics.Process.Start(topath + #"\allfiles.txt");
}

Blueimp jquery file upload alternative file names cause the preview missing

The client can preview before uploading but cannot see after uploding as the local language file name.
So I want to rename the filename before upload the image for it can preview before and after uploading.
reference the https://github.com/blueimp/jQuery-File-Upload/wiki/API
How to provide alternative file names:
The name property of File objects is read only, but an alternative name can be provided as uploadName property for each individual file:
$('#fileupload').fileupload({
add: function (e, data) {
var count = data.files.length;
var i;
for (i = 0; i < count; i++) {
data.files[i].uploadName =
Math.floor(Math.random() * 1000) + '_' + data.files[i].name;
}
data.submit();
}
});
When I use above js function the preview is good function when the image add and submit to server immediately~
But when I delete
data.submit();
as I want client to decide if upload or not before uploading.
the preview image function is missing!
and cannot upload any file.
Does any code can rename for the unicode filename (as the local language encoing filename cannot show on the preview function when it uploaded) before submit to the server?
jquery.fileupload.js
....
} else {
$.each(options.files, function (index, file) {
// This check allows the tests to run with
// dummy objects:
if (that._isInstanceOf('File', file) ||
that._isInstanceOf('Blob', file)) {
formData.append(
($.type(options.paramName) === 'array' &&
options.paramName[index]) || paramName,
file,
file.uploadName || file.name
);
}
});
}
}
change the last
file.uploadName || file.name
to
encodeURI(file.uploadName || file.name)
then can see the write decode in the back
fileName = URLDecoder.decode(fileDetail.getFileName(), "UTF-8");

Using Word.SearchOptions in an Office Add-In

I am developing an Office Add-In that processes the text of each paragraph of a Word document against data in JSON format and writes the result to a within the index.html file that is rendered in the Task Pane. This works fine. I am now trying to format the strings within the Word document that correspond to the hits for the keys in the JSON data.
I have a JS block in the head of the index.html file in which I call "Office.initialize" and define a variable based on the JSON data, and have utility functions related to the above functionality. After that comes a function in which I get the Word context and process the Word file paragraphs against the JSON data, and then try to search the Word paragraph itself in order to format the hits. In this last task I am trying to reproduce a snippet from Michael Mainer 1. But no formatting happens when I activate this function. Unfortunately I don't have access to a console since I am on a Mac, which makes it harder to debug.
I would be very appreciative of someone showing me where I'm going wrong.
`function tester() {
Word.run(function (context) {
// Create a proxy object for the document's paragraphs
var paragraphs = context.document.body.paragraphs;
// Load the paragraphs' text, which I run regexes on
context.load(paragraphs, 'text');
return context.sync().then(function () {
for (var i = 0; i < paragraphs.items.length; i++) {
var text = paragraphs.items[i].text;
// jquery to iterate over the "notes" objects from the JSON
$.each(notes, function(key, value) {
var regex = new RegExp("\\b" + key + "\\b", "g");
var res = regex.test(text);
// if the regex hits...
if (res == true) {
// This part works fine, using the JSON data to append to the <DIV> with ID = "notes"
document.getElementById('notes').innerHTML += "<button onclick=hide('" + value.seqNo + "')><b>" + key + "</b></button><p class='" + value.seqNo + "' id='" + i + "'>" + value.notes[0].note + "</p>";
// I now go on to searching for these hits within the current paragraph in the Word file
var thisPara = paragraphs.items[i];
// Set up the search options.
var options = Word.SearchOptions.newObject(context);
options.matchCase = false
// Queue the commmand to search the current paragraph for occurrences of the string "key" (coming from the JSON data)
var searchResults = paragraphs.items[i].search(key, options);
// Load 'text' and 'font' for searchResults.
context.load(searchResults, 'text, font');
// Synchronize the document state by executing the queued-up commands, and return a promise to indicate task completion.
return context.sync().then(function () {
// Queue a command to change the font for each found item.
for (var j = 0; j < searchResults.items.length; j++) {
searchResults.items[j].font.color = '#FF0000'
searchResults.items[j].font.highlightColor = '#FFFF00';
searchResults.items[j].font.bold = true;
}
// Synchronize the document state by executing the queued-up commands,
// and return a promise to indicate task completion.
return context.sync();
});
}
});
}
});
})
.catch(function (error) {
console.log('Error: ' + JSON.stringify(error));
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
});
}
`
It looks like you just need access to the font property, have you tried just:
context.load(searchResults, 'font');
That was working for me?

In an Eclipse plugin, how can I make a DirectoryFieldEditor start with a particular path?

I am making an Eclipse plugin which on right clicking a project produces a UI.
In this UI I have used DirectoryFieldEditor. This produces directory dialog starting at "MyComputer" as root. What i want is it to show paths starting at the project which i right clicked. how can this be achieved?
I am trying to mimic when you right click a project and say "new package" - the source folder browse give a directory dialog with only those folders which are open projects.... I want a similar directory dialog.
Can somebody help and give me some code snippets or suggestions?
Well, considering the "new package" is actually the class:
org.eclipse.jdt.internal.ui.wizards.NewPackageCreationWizard
which uses NewPackageWizardPage (source code), you will see:
public void init(IStructuredSelection selection) {
IJavaElement jelem = getInitialJavaElement(selection);
initContainerPage(jelem);
String pName = ""; //$NON-NLS-1$
if (jelem != null) {
IPackageFragment pf = (IPackageFragment) jelem
.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
if (pf != null && !pf.isDefaultPackage())
pName = pf.getElementName();
}
setPackageText(pName, true);
updateStatus(new IStatus[] { fContainerStatus, fPackageStatus });
}
With the getInitialJavaElement() being part of superclass NewContainerWizardPage:
/**
* Utility method to inspect a selection to find a Java element.
*
* #param selection the selection to be inspected
* #return a Java element to be used as the initial selection, or <code>null</code>,
* if no Java element exists in the given selection
*/
protected IJavaElement getInitialJavaElement(
IStructuredSelection selection) {
IJavaElement jelem = null;
if (selection != null && !selection.isEmpty()) {
Object selectedElement = selection.getFirstElement();
if (selectedElement instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) selectedElement;
jelem = (IJavaElement) adaptable
.getAdapter(IJavaElement.class);
if (jelem == null) {
IResource resource = (IResource) adaptable
.getAdapter(IResource.class);
if (resource != null
&& resource.getType() != IResource.ROOT) {
while (jelem == null
&& resource.getType() != IResource.PROJECT) {
resource = resource.getParent();
jelem = (IJavaElement) resource
.getAdapter(IJavaElement.class);
}
if (jelem == null) {
jelem = JavaCore.create(resource); // java project
}
}
}
}
}
if (jelem == null) {
IWorkbenchPart part = JavaPlugin.getActivePage()
.getActivePart();
if (part instanceof ContentOutline) {
part = JavaPlugin.getActivePage().getActiveEditor();
}
if (part instanceof IViewPartInputProvider) {
Object elem = ((IViewPartInputProvider) part)
.getViewPartInput();
if (elem instanceof IJavaElement) {
jelem = (IJavaElement) elem;
}
}
}
if (jelem == null
|| jelem.getElementType() == IJavaElement.JAVA_MODEL) {
try {
IJavaProject[] projects = JavaCore.create(
getWorkspaceRoot()).getJavaProjects();
if (projects.length == 1) {
jelem = projects[0];
}
} catch (JavaModelException e) {
JavaPlugin.log(e);
}
}
return jelem;
}
Between those two methods, you should be able to initialize your custom UI with the exact information (i.e., "relative source path") you want.
If you look at the source of DirectoryFieldEditor, you will see it open its directory chooser dialog based on the value if its main Text field defined in StringFieldEditor:doLoad():
String JavaDoc value = getPreferenceStore().getString(getPreferenceName());
textField.setText(value);
That means you need, in your custom UI, to get the preference store and associate the right path with an id. You will use that id for your DirectoryFieldEditor initialization. Y oucan see an example here.
public static final String MY_PATH = "my.init.path";
IPreferenceStore store = myPlugin.getDefault().getPreferenceStore();
store.setValue(MY_PATH, theRightPath);
myDirFieldEditor = new DirectoryFieldEditor(MY_PATH, "&My path", getFieldEditorParent());
As you mention in the comments, all this will only initialize the eclipse-part GUI, not the native windows explorer launched by a DirectoryDialog:
this (the native interface) is based on:
parameters stored in BROWSEINFO Structure
used by the actual GUI SHBrowseForFolder Function, which actually displays a dialog box that enables the user to select a Shell folder.
That GUI initialize a root path based on filter path, so you need to also initialize (on eclipse side) that filter field with a path in order to get it pick up by the Windows-GUI SHBrowseForFolder.
According to DirectoryFieldEditor, that is exactly what getTextControl() (the field you initialized above) is for.
But the problem comes from the fact that field has been initialized with a relative path. Since that path is unknown by the underlying OS, it defaults back to root OS path.
You need to find a way to store the full system path of the project, not the relative path.
That way, that full path will be recognized by the Os and used as initial path.
For instance, from a IJavaElement, you can get its associated resource, and try to get the (full system) path from there.
Actually, from the IPackageFragment you should be able to call getPath(), and check if the IPath returned contains the full system path.