Skip to content

Commit

Permalink
noNamespaceSchemaLocation file missing CodeAction
Browse files Browse the repository at this point in the history
A CodeAction for the error `schema-reference.4` that
creates the file referenced in
`xsi:noNamespaceSchemaLocation` if its missing.

Closes eclipse-lemminx#691

Signed-off-by: David Thompson davthomp@redhat.com
  • Loading branch information
datho7561 committed Jun 16, 2020
1 parent 022615e commit 3ec2894
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@

import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.CreateFile;
import org.eclipse.lsp4j.CreateFileOptions;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.ResourceOperation;
import org.eclipse.lsp4j.TextDocumentEdit;
import org.eclipse.lsp4j.TextDocumentItem;
import org.eclipse.lsp4j.TextEdit;
Expand All @@ -36,6 +40,9 @@
*/
public class CodeActionFactory {

// TODO: register on the client side so that the action can be performed
public static String APPLY_WORKSPACE_EDIT = "xml.apply.workspaceEdit";

/**
* Create a CodeAction to remove the content from the given range.
*
Expand Down Expand Up @@ -117,4 +124,33 @@ public static CodeAction replaceAt(String title, String replaceText, TextDocumen
insertContentAction.setEdit(workspaceEdit);
return insertContentAction;
}

/**
* Makes a CodeAction to create a file and add content to the file.
*
* @param title The displayed name of the CodeAction
* @param docURI The file to create
* @param content The text to put into the newly created document.
* @param diagnostic The diagnostic that this CodeAction will fix
*/
public static CodeAction createDocumentWith(String title, String docURI, String content, Diagnostic diagnostic) {

List<Either<TextDocumentEdit, ResourceOperation>> actionsToTake = new ArrayList<>(2);

actionsToTake.add(Either.forRight(new CreateFile(docURI, new CreateFileOptions(false, true))));

VersionedTextDocumentIdentifier identifier = new VersionedTextDocumentIdentifier(docURI, 0);
TextEdit te = new TextEdit(new Range(new Position(0, 0), new Position(0, 0)), content);
actionsToTake.add(Either.forLeft(new TextDocumentEdit(identifier, Collections.singletonList(te))));

WorkspaceEdit createAndAddContentEdit = new WorkspaceEdit();
createAndAddContentEdit.setDocumentChanges(actionsToTake);

CodeAction codeAction = new CodeAction(title);
codeAction.setEdit(createAndAddContentEdit);
codeAction.setDiagnostics(Collections.singletonList(diagnostic));
codeAction.setKind(CodeActionKind.QuickFix);

return codeAction;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.cvc_complex_type_4CodeAction;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.cvc_enumeration_validCodeAction;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.cvc_type_3_1_1CodeAction;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.schema_reference_4CodeAction;
import org.eclipse.lemminx.services.extensions.ICodeActionParticipant;
import org.eclipse.lemminx.services.extensions.diagnostics.IXMLErrorCode;
import org.eclipse.lemminx.utils.DOMUtils;
Expand Down Expand Up @@ -269,5 +270,8 @@ public static void registerCodeActionParticipants(Map<String, ICodeActionPartici
codeActions.put(cvc_complex_type_2_1.getCode(), new cvc_complex_type_2_1CodeAction());
codeActions.put(TargetNamespace_1.getCode(), new TargetNamespace_1CodeAction());
codeActions.put(TargetNamespace_2.getCode(), new TargetNamespace_2CodeAction());

// TODO: check if the client has the correct capabilities to execute this code action
codeActions.put(schema_reference_4.getCode(), new schema_reference_4CodeAction());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.eclipse.lemminx.extensions.contentmodel.participants.codeactions;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.lemminx.commons.CodeActionFactory;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.services.extensions.ICodeActionParticipant;
import org.eclipse.lemminx.services.extensions.IComponentProvider;
import org.eclipse.lemminx.settings.SharedSettings;
import org.eclipse.lemminx.utils.StringUtils;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Range;

/**
* Code Action that creates a schema file referenced by xsi:noNamespaceSchemaLocation if it is missing
*/
public class schema_reference_4CodeAction implements ICodeActionParticipant {

private static final Pattern PATH_FINDING_REGEX = Pattern.compile("[^']+'file://(.+)',.*");

@Override
public void doCodeAction(Diagnostic diagnostic, Range range, DOMDocument document, List<CodeAction> codeActions,
SharedSettings sharedSettings, IComponentProvider componentProvider) {

String missingFilePath = getPathFromDiagnostic(diagnostic);
if (StringUtils.isEmpty(missingFilePath)) {
return;
}
Path p = Paths.get(missingFilePath);
if (p.toFile().exists()) {
return;
}

// TODO: use the generator
String schemaTemplate = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n\n</xs:schema>";

CodeAction makeSchemaFile = CodeActionFactory.createDocumentWith(
"Generate missing file '" + p.toFile().getName() + "'",
missingFilePath,
schemaTemplate,
diagnostic);

codeActions.add(makeSchemaFile);
}

/**
* Extract the file to create from the diagnostic. Example diagnostic:
* schema_reference.4: Failed to read schema document
* 'file:///home/dthompson/Documents/TestFiles/potationCoordination.xsd',
* because 1) could not find the document; 2) the document could not be read;
* 3) the root element of the document is not <xsd:schema>.
*/
private String getPathFromDiagnostic(Diagnostic diagnostic) {
Matcher m = PATH_FINDING_REGEX.matcher(diagnostic.getMessage());
if (m.find()) {
return m.group(1);
}
return null;
}

}

0 comments on commit 3ec2894

Please sign in to comment.