1 module served.linters.dfmt; 2 3 import std.algorithm; 4 import std.array; 5 import std.conv; 6 import std.file; 7 import std.json; 8 import std.path; 9 import std.string; 10 11 import served.extension; 12 import served.linters.diagnosticmanager; 13 import served.types; 14 15 import workspaced.api; 16 import workspaced.coms; 17 18 import workspaced.com.dfmt; 19 20 static immutable string DfmtDiagnosticSource = "dfmt"; 21 22 enum DiagnosticSlot = 2; 23 24 void lint(Document document) 25 { 26 auto fileConfig = config(document.uri); 27 if (!fileConfig.d.enableFormatting) 28 return; 29 30 if (!backend.has!DfmtComponent) 31 return; 32 auto dfmt = backend.get!DfmtComponent; 33 34 createDiagnosticsFor!DiagnosticSlot(document.uri) = lintDfmt(dfmt, document); 35 updateDiagnostics(document.uri); 36 } 37 38 private Diagnostic[] lintDfmt(DfmtComponent dfmt, ref Document document) 39 { 40 auto instructions = dfmt.findDfmtInstructions(document.rawText); 41 42 auto diagnostics = appender!(Diagnostic[]); 43 bool fmtOn = true; 44 45 Position positionCache; 46 size_t byteCache; 47 48 void setFmt(DfmtInstruction instruction, bool on) 49 { 50 if (fmtOn == on) 51 { 52 } 53 fmtOn = on; 54 } 55 56 foreach (DfmtInstruction instruction; instructions) 57 { 58 Diagnostic d; 59 d.source = DfmtDiagnosticSource; 60 auto start = document.movePositionBytes(positionCache, byteCache, instruction.index); 61 auto end = document.movePositionBytes(start, instruction.index, instruction.index + instruction.length); 62 positionCache = end; 63 byteCache = instruction.index + instruction.length; 64 d.range = TextRange(start, end); 65 66 final switch (instruction.type) 67 { 68 case DfmtInstruction.Type.dfmtOn: 69 case DfmtInstruction.Type.dfmtOff: 70 bool on = instruction.type == DfmtInstruction.Type.dfmtOn; 71 if (on == fmtOn) 72 { 73 d.message = on ? "Redundant `dfmt on`" : "Redundant `dfmt off`"; 74 d.code = JsonValue(on ? "redundant-on" : "redundant-off"); 75 d.severity = DiagnosticSeverity.hint; 76 d.tags = [DiagnosticTag.unnecessary]; 77 diagnostics ~= d; 78 } 79 fmtOn = on; 80 break; 81 case DfmtInstruction.Type.unknown: 82 d.message = "Not a valid dfmt command (try `//dfmt off` or `//dfmt on` instead)"; 83 d.code = JsonValue("unknown-comment"); 84 d.severity = DiagnosticSeverity.warning; 85 diagnostics ~= d; 86 break; 87 } 88 } 89 90 return diagnostics.data; 91 } 92 93 void clear() 94 { 95 diagnostics[DiagnosticSlot] = null; 96 updateDiagnostics(); 97 } 98 99 @("misspelling on/off") 100 unittest 101 { 102 auto dfmt = new DfmtComponent(); 103 dfmt.workspaced = new WorkspaceD(); 104 Document d = Document.nullDocument(`void foo() { 105 //dfmt offs 106 int i = 5; 107 //dfmt onf 108 }`); 109 auto linted = lintDfmt(dfmt, d); 110 assert(linted.length == 2); 111 assert(linted[0].severity.deref == DiagnosticSeverity.warning); 112 assert(linted[1].severity.deref == DiagnosticSeverity.warning); 113 } 114 115 @("redundant on/off") 116 unittest 117 { 118 auto dfmt = new DfmtComponent(); 119 dfmt.workspaced = new WorkspaceD(); 120 Document d = Document.nullDocument(`void foo() { 121 //dfmt on 122 //dfmt off 123 int i = 5; 124 //dfmt off 125 //dfmt ons 126 }`); 127 auto linted = lintDfmt(dfmt, d); 128 import std.stdio; stderr.writeln("diagnostics:\n", linted); 129 assert(linted.length == 3); 130 assert(linted[0].severity.deref == DiagnosticSeverity.hint); 131 assert(linted[1].severity.deref == DiagnosticSeverity.hint); 132 assert(linted[2].severity.deref == DiagnosticSeverity.warning); 133 }