I use a WizardNewFileCreationPage to give a user the option to create a new file the project workspace.
However I want the user to create the file only in certain folders, for example in project/data not in project/data/trash folder.
How do I specify this criteria?
You can create a class which extends WizardNewFileCreationPage and override the validatePage method:
#Override
protected boolean validatePage()
{
boolean valid = super.validatePage();
if (!valid)
return false;
IPath containerPath = getContainerFullPath();
String fileName = getFileName();
valid = ... check containerPath and fileName meet your criteria
if (!valid)
{
setErrorMessage("your error message");
}
return valid;
}
Related
Background
I created an extension that interacts with documents. In order to test the extension I need to create documents, that the extension can work with. The extension has to access the document via uri.
Currently I'm using vscode.workspace.openTextDocument({content: _content, language: _language}); for document creation. The problem is, it does not have a valid URI.
Question
How can I create a virtual document in memory, that has a valid URI?
As there was no native solution to this, I created my and I'd like to share it here:
A TextDocumentContentProvider for files in memory. Example usage shown below
memoryfile.ts
import * as vscode from 'vscode';
const _SCHEME = "inmemoryfile";
/**
* Registration function for In-Memory files.
* You need to call this once, if you want to make use of
* `MemoryFile`s.
**/
export function register_memoryFileProvider ({ subscriptions }: vscode.ExtensionContext)
{
const myProvider = new (class implements vscode.TextDocumentContentProvider
{
provideTextDocumentContent(uri: vscode.Uri): string
{
let memDoc = MemoryFile.getDocument (uri);
if (memDoc == null)
return "";
return memDoc.read ();
}
})();
subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(
_SCHEME, myProvider));
}
/**
* Management class for in-memory files.
**/
class MemoryFileManagement
{
private static _documents: {[key: string]: MemoryFile} = {};
private static _lastDocId: number = 0;
public static getDocument(uri: vscode.Uri) : MemoryFile | null
{
return MemoryFileManagement._documents[uri.path];
}
private static _getNextDocId(): string{
MemoryFileManagement._lastDocId++;
return "_" + MemoryFileManagement._lastDocId + "_";
}
public static createDocument(extension = "")
{
let path = MemoryFileManagement._getNextDocId ();
if (extension != "")
path += "." + extension;
let self = new MemoryFile(path);
MemoryFileManagement._documents[path] = self;
return self;
}
}
/**
* A file in memory
**/
export class MemoryFile
{
/******************
** Static Area **
******************/
public static getDocument(uri: vscode.Uri) : MemoryFile | null {
return MemoryFileManagement.getDocument (uri);
}
public static createDocument(extension = "") {
return MemoryFileManagement.createDocument (extension);
}
/******************
** Object Area **
******************/
public content: string = "";
public uri: vscode.Uri;
constructor (path: string)
{
this.uri = vscode.Uri.from ({scheme: _SCHEME, path: path})
}
public write(strContent: string){
this.content += strContent;
}
public read(): string {
return this.content;
}
public getUri(): vscode.Uri {
return this.uri;
}
}
Example usage
Register the provider
You need to register the provider somewhere in the beginning of your test code (I do it in index.ts before Mocha is instantiated):
register_memoryFileProvider (extensionContext);
(How do I get the extension context?)
Create a document
Creating and using a file works as follows:
// create the in-memory document
let memfile = MemoryFile.createDocument ("ts");
memfile.write ("my content");
// create a vscode.TextDocument from the in-memory document.
let doc = await vscode.workspace.openTextDocument (memfile.getUri ());
Notes
Be aware, that LSP commands might not work with with approach, because they might be registered to a certain specific schema.
As rioV8 said, you can also use an existing document and change its content. Here the code:
export class TmpFile
{
private static _lastDocId: number = 0;
private static _getNextDocId(): string{
this._lastDocId++;
return "tmpfile_" + this._lastDocId;
}
public static async createDocument(strContent: string, extension:string = "")
: Promise<vscode.TextDocument | null>
{
let folder = "/tmp"
let filename = this._getNextDocId ();
let ext = (extension != "" ? "." + extension : "");
const newFile = vscode.Uri.parse('untitled:' + path.join(folder, filename + ext));
{
const edit = new vscode.WorkspaceEdit();
edit.insert(newFile, new vscode.Position(0, 0), strContent);
let success = await vscode.workspace.applyEdit(edit);
if (!success)
return null;
}
let document = await vscode.workspace.openTextDocument(newFile);
return document;
}
}
Pro's
It's a file (schema), so all LSP commands will work
The path (used above) does not even need to exist.
Con's
The file is really opened in the editor. You need to close it later
The file is a changed file in the editor, so it will ask you to save the changes upon closing.
Files cannot be closed in vscode. You can only run:
vscode.window.showTextDocument(doc.uri, {preview: true, preserveFocus: false})
.then(() => {
return vscode.commands.executeCommand('workbench.action.closeActiveEditor');
});
```<br>
which is a rather nasty workaround.
In my custom plugin I am creating some folders on a new Wizard and adding paths of those created folders in project preferences. Now while deleting the folder I need to remove that path from the store. For that I extended org.eclipse.ltk.core.refactoring.deleteParticipants extension. I also created a class extending Change class and inside it in the perform method I wrote following to remove the key-value from preferences.
public Change perform(IProgressMonitor pm) throws CoreException {
System.out.println("called");
IPath deletedFolderPath = deletedFolder.getProjectRelativePath().makeAbsolute();
IResource[] roots = {deletedFolder.getProject()};
String[] fileSearchPatterns = {"*.properties", "*.prefs"};
FileTextSearchScope searchScope = FileTextSearchScope.newSearchScope(roots, fileSearchPatterns, false);
String regex = deletedFolderPath.addTrailingSeparator().toString();
Pattern searchPattern = Pattern.compile(regex);
TextSearchRequestor requestor = new TextSearchRequestor() {
public boolean acceptPatternMatch(TextSearchMatchAccess matchAccess) throws CoreException {
IFile matchedFile = matchAccess.getFile();
if("prefs".equals(matchedFile.getFileExtension())){
String prefKey = "";
try {
prefKey = ProjectPreferences.getKeyForValue(regex, deletedFolder.getProject());
ProjectPreferences.removeKeyFromStore(prefKey, deletedFolder.getProject());
System.out.println("after");
}
catch (BackingStoreException e) {
throw new CoreException(null);
}
}
else if(".properties".equals(matchedFile.getFileExtension())) {
}
return true;
}
};
TextSearchEngine.create().search(searchScope, requestor, searchPattern, pm);
return null;
}
but it get stuck on the line after pressing "OK" on the delete wizard.
ProjectPreferences.removeKeyFromStore(prefKey, deletedFolder.getProject())
or more specifically on the flush statement written in the removeKeyFromStore method
public static void removeKeyFromStore(String key, IProject project) throws BackingStoreException {
IEclipsePreferences projPref = getPreferences(project);
projPref.remove(key);
projPref.flush();
}
What am I doing wrong here or any other better way to achieve the same functionality.
Thanks!!
I want to create my own SonarQube Plugin for the RPG language. I have the following problem.
I start by created the RpgLanguage class that extends to AbstractLanguage. In this class, I defined my new language "Rpg". You can see my class in the following code :
public class RpgLanguage extends AbstractLanguage{
public static final String KEY = "rpg";
private Settings settings;
public RpgLanguage(Settings settings) {
super(KEY, "Rpg");
this.settings = settings;
}
public String[] getFileSuffixes() {
String[] suffixes = settings.getStringArray("");
if (suffixes == null || suffixes.length == 0) {
suffixes = StringUtils.split(".RPG", ",");
}
return suffixes;
}
}
After, I have created my RpgRulesDefinition class that implements RulesDefinition. In this class, I create a new repository for the language RPG and I want to add a rule in this repository (empty rules). The code is like below :
public static final String REPOSITORY_KEY = "rpg_repository_mkoza";
public void define(Context context) {
NewRepository repo = context.createRepository(REPOSITORY_KEY, "rpg");
repo.setName("Mkoza Analyser rules RPG");
// We could use a XML or JSON file to load all rule metadata, but
// we prefer use annotations in order to have all information in a single place
RulesDefinitionAnnotationLoader annotationLoader = new RulesDefinitionAnnotationLoader();
annotationLoader.load(repo, RpgFileCheckRegistrar.checkClasses());
repo.done();
}
My class RpgFileCheckRegistrar that call my Rules :
/**
* Register the classes that will be used to instantiate checks during analysis.
*/
public void register(RegistrarContext registrarContext) {
// Call to registerClassesForRepository to associate the classes with the correct repository key
registrarContext.registerClassesForRepository(RpgRulesDefinition.REPOSITORY_KEY, Arrays.asList(checkClasses()), Arrays.asList(testCheckClasses()));
}
/**
* Lists all the checks provided by the plugin
*/
public static Class<? extends JavaCheck>[] checkClasses() {
return new Class[] {
RulesExampleCheck.class
};
}
/**
* Lists all the test checks provided by the plugin
*/
public static Class<? extends JavaCheck>[] testCheckClasses() {
return new Class[] {};
}
My Rule class (still empty):
#Rule(
key = "Rule1",
name = "Rule that make nothing",
priority = Priority.MAJOR,
tags = {"example"}
)
public class RulesExampleCheck extends BaseTreeVisitor{
/**
* Right in java code your rule
*/
}
And the class SonarPlugin that call all these extensions :
public final class RpgSonarPlugin extends SonarPlugin
{
// This is where you're going to declare all your Sonar extensions
public List getExtensions() {
return Arrays.asList(
RpgLanguage.class,
RpgRulesDefinition.class,
RpgFileCheckRegistrar.class
);
}
}
The problem when I want to start the server sonar, I obtain this error stack :
Exception sending context initialized event to listener instance of class org.sonar.server.platform.PlatformServletContextListener
java.lang.IllegalStateException: One of HTML description or Markdown description must be defined for rule [repository=rpg_repository_mkoza, key=Rule1]
I try different things but I don't understand why there are these error.
Of course I want that my repository "rpg_repository_mkoza" is display in the RPG's repository in SonarQube with the Rules : RulesExampleCheck.
My sonar-plugin-version is the 3.7.1
I find my problem. There are need to add the field 'description' in #Rule.
For example :
#Rule(
key = "Rule1",
name = "RuleExampleCheck",
description = "This rule do nothing",
priority = Priority.INFO,
tags = {"try"}
)
I am using wickets 1.5.I have registration page where fileds has to be validate for only numbers(like phone no) . i have a validation class as below
public class Validator implements IValidator<String> {
Pattern pattern;
public Validator() {
pattern = Pattern.compile("[0-9]+");
}
public void validate(IValidatable<String> validatable) {
final String field = validatable.getValue();
if (pattern.matcher(field).matches() == false) {
error(validatable, "phoneno" );
}
}
private void error(IValidatable<String> validatable, String errorKey) {
ValidationError error = new ValidationError();
error.addMessageKey(getClass().getSimpleName() + "." + errorKey);
validatable.error(error);
}
}
I have my Registration.properites file in the same package the Registation.html and .java files are there.
My Registration.properites is
Registration.phoneno= Please enter numbers only
I am calling this in my wicket class
phoneno.add(new Validator());
I am getting below error
Could not locate error message for component: TextField#sendform:phoneno and error: [ValidationError message=[null], keys=[Validator.phoneno], variables=[null]]. Tried keys: phoneno.Validator.phoneno, Validator.phoneno.
What i am doing wrong ?
I have done this as per below link
http://www.mkyong.com/wicket/create-custom-validator-in-wicket/
I think you aren't specifying the property key correctly. The getClass().getSimpleName() method is returning "Validator", when your key starts with 'Registration'. Just try this:
error.addMessageKey("Registration.phoneno");
I wrote an Eclipse Plugin that basically allow a programmer to select a Java source from the Project Explorer and by selecting the corresponding DropDown menu option it will creates an interface .java file based on the one selected.
Everything works fine, but now I need to program the update part of the job.
The update requierement is simple, I need to listen for changes and identify that the sources that have the interface generated have been modified and recreate the interface file.
To do this I wrote a class that implements IResourceChangeListener interface.
That class looks like:
public class DTOChangeListener implements IResourceChangeListener {
private List<UpdatedUnit> updatedUnits;
public DTOChangeListener() {
super();
this.updatedUnits=new ArrayList<UpdatedUnit>();
}
#Override
public void resourceChanged(IResourceChangeEvent event) {
try{
if(event.getType() == IResourceChangeEvent.POST_CHANGE){
event.getDelta().accept(this.buildVisitor());
}
}catch(CoreException ex){
ex.printStackTrace();
}
}
protected IResourceDeltaVisitor buildVisitor(){
IResourceDeltaVisitor result=new IResourceDeltaVisitor() {
#Override
public boolean visit(IResourceDelta resDelta) throws CoreException {
String resName=resDelta.getResource().getName();
if(resName==null || resName.equals("")){
return true;
}
String[] splits=resName.split("\\.");
String name = splits[0];
if(name.contains("PropertyAccess")){
return false;
}
String interfaceName=name + "PropertyAccess";
String interfaceFile=interfaceName + ".java";
IResource res=resDelta.getResource();
if((res instanceof IFolder) || (res instanceof IProject)){
// Avoid Folder & Project Nodes
return true;
}
IProject project=res.getProject();
if(project!=null){
if(project.isNatureEnabled("org.eclipse.jdt.core.javanature")){
IJavaElement element=JavaCore.create(res);
if(element instanceof ICompilationUnit){
ICompilationUnit unit=(ICompilationUnit)element;
IPath path=res.getProjectRelativePath().removeLastSegments(1);
IResource propertyAccess=project.findMember(path.append(interfaceFile));
if(propertyAccess!=null){
UpdatedUnit updatedUnit=new UpdatedUnit(project, path, unit);
updatedUnits.add(updatedUnit);
return false;
}
}
}
}
return true;
}
};
return result;
}
public List<UpdatedUnit> getUpdatedUnits() {
return updatedUnits;
}
}
I add the Listener to the Workspace, now the question I have is:
How can I know when the updatedUnits List is completed in order to proccess the list with my own code?
One posible answer to this question would be, don't worry, the:
event.getData().accept(this.buildVisitor());
will block until proccessing of the visitor finish.
but at least is not documented like it would.
Any ideas would be appreciated.
Thanks in Advance.
Daniel
Unless it's documented to not block, it blocks.