All files / providers/refactoring xsd-any-uri-code-action-provider.ts

72.22% Statements 26/36
50% Branches 4/8
83.33% Functions 5/6
70.58% Lines 24/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117                      15x                   15x   15x                       6x   48x   6x                     4x     4x   4x 2x   2x       2x   2x         2x 2x   2x 2x 2x   2x       4x 2x     2x                                           4x               2x    
import * as vscode from 'vscode';
import { container } from 'tsyringe';
import { ServiceToken } from '@src/services/tokens';
import { XSD_ANY_URI_LITERAL_CODE } from '@src/languages/linters/xsd-any-uri-literal-linter';
 
export { XSD_ANY_URI_LITERAL_CODE };
 
/**
 * Regex to extract the URI value from the diagnostic message produced by `XsdAnyUriLiteralLinter`.
 * Matches: `Use the IRI reference '<http://...>' instead of a typed string literal.`
 */
const IRI_FROM_MESSAGE_REGEX = /^Use the IRI reference '<(.+)>'/;
 
/**
 * Provides Quick Fix code actions for `"http(s)://..."^^xsd:anyURI` diagnostics
 * emitted by the language server's {@link XsdAnyUriLiteralLinter}.
 *
 * This provider does **not** scan the document itself — it reacts to diagnostics
 * that already carry the {@link XSD_ANY_URI_LITERAL_CODE} code.
 */
export class XsdAnyUriCodeActionProvider implements vscode.CodeActionProvider {
	static readonly providedCodeActionKinds = [vscode.CodeActionKind.QuickFix];
 
	static readonly languages = [
		'ntriples',
		'nquads',
		'turtle',
		'n3',
		'trig',
		'sparql',
		'xml',
		'datalog'
	];
 
	constructor() {
		const context = container.resolve<vscode.ExtensionContext>(ServiceToken.ExtensionContext);
 
		const selector = XsdAnyUriCodeActionProvider.languages.map(language => ({ language }));
 
		context.subscriptions.push(
			vscode.languages.registerCodeActionsProvider(selector, this, {
				providedCodeActionKinds: XsdAnyUriCodeActionProvider.providedCodeActionKinds,
			}),
		);
	}
 
	/**
	 * Provides Quick Fix code actions for xsd:anyURI string literals.
	 */
	provideCodeActions(document: vscode.TextDocument, _range: vscode.Range | vscode.Selection, actionContext: vscode.CodeActionContext): vscode.CodeAction[] {
		const actions: vscode.CodeAction[] = [];
 
		// Collect all xsd:anyURI diagnostics in the action context (cursor-intersecting).
		const contextDiagnostics = actionContext.diagnostics.filter(d => d.code === XSD_ANY_URI_LITERAL_CODE);
 
		for (const diagnostic of contextDiagnostics) {
			const iriValue = extractIriFromMessage(diagnostic.message);
 
			Iif (!iriValue) {
				continue;
			}
 
			const newText = `<${iriValue}>`;
 
			const fix = new vscode.CodeAction(
				`Replace with IRI reference ${newText}`,
				vscode.CodeActionKind.QuickFix,
			);
 
			const edit = new vscode.WorkspaceEdit();
			edit.replace(document.uri, diagnostic.range, newText);
 
			fix.edit = edit;
			fix.diagnostics = [diagnostic];
			fix.isPreferred = true;
 
			actions.push(fix);
		}
 
		// "Fix all" action when there are multiple such diagnostics in the whole document.
		if (contextDiagnostics.length > 0) {
			const allDiagnostics = vscode.languages.getDiagnostics(document.uri)
				.filter(d => d.code === XSD_ANY_URI_LITERAL_CODE);
 
			Iif (allDiagnostics.length > 1) {
				const fixAll = new vscode.CodeAction(
					`Replace all xsd:anyURI literals with IRI references (${allDiagnostics.length})`,
					vscode.CodeActionKind.QuickFix,
				);
 
				const edit = new vscode.WorkspaceEdit();
 
				for (const d of allDiagnostics) {
					const iri = extractIriFromMessage(d.message);
 
					if (iri) {
						edit.replace(document.uri, d.range, `<${iri}>`);
					}
				}
 
				fixAll.edit = edit;
 
				actions.push(fixAll);
			}
		}
 
		return actions;
	}
}
 
/**
 * Extracts the IRI value from an {@link XsdAnyUriLiteralLinter} diagnostic message.
 */
function extractIriFromMessage(message: string): string | undefined {
	return message.match(IRI_FROM_MESSAGE_REGEX)?.[1];
}