• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

EduardSergeev / vscode-haskutil / 9606842810

21 Jun 2024 01:30AM UTC coverage: 97.326% (+1.3%) from 96.059%
9606842810

Pull #89

github

web-flow
Merge 893af7cc0 into 3479e6943
Pull Request #89: Sort imported element lists on `Organize imports`

280 of 297 branches covered (94.28%)

89 of 93 new or added lines in 3 files covered. (95.7%)

12 existing lines in 6 files now uncovered.

1274 of 1309 relevant lines covered (97.33%)

15.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.68
/src/features/organizeImportProvider.ts
1
import { CodeActionProvider, Disposable, TextDocument, Range, CodeActionContext, CancellationToken, CodeAction, WorkspaceEdit, CodeActionKind, Diagnostic, DiagnosticSeverity, WorkspaceConfiguration, TextDocumentWillSaveEvent } from 'vscode';
1✔
2
import * as vscode from 'vscode';
1✔
3
import ImportDeclaration from './importProvider/importDeclaration';
1✔
4
import Configuration from '../configuration';
1✔
5

1✔
6

1✔
7
export default class OrganizeImportProvider implements CodeActionProvider {
1✔
8
  public static commandId: string = 'haskell.organizeImports';
1✔
9
  private diagnosticCollection: vscode.DiagnosticCollection;
1✔
10
  private static diagnosticCode: string = "haskutil.unorganizedImports";
1✔
11

1✔
12
  public activate(subscriptions: Disposable[]) {
1✔
13
    const command = vscode.commands.registerCommand(OrganizeImportProvider.commandId, this.runCodeAction, this);
1✔
14
    subscriptions.push(command);
1✔
15

1✔
16
    this.diagnosticCollection = vscode.languages.createDiagnosticCollection();
1✔
17
    subscriptions.push(this.diagnosticCollection);
1✔
18
    vscode.workspace.onDidOpenTextDocument(this.checkImports, this, subscriptions);
1✔
19
    vscode.workspace.onDidCloseTextDocument(doc => this.diagnosticCollection.delete(doc.uri), null, subscriptions);
1✔
20
    vscode.workspace.onDidSaveTextDocument(this.checkImports, this, subscriptions);
1✔
21
    vscode.workspace.onWillSaveTextDocument(this.ensureOrganized, this, subscriptions);
1✔
22
    vscode.workspace.textDocuments.filter(d => d.languageId === 'haskell').forEach(this.checkImports, this);
1✔
23
  }
1✔
24

1✔
25
  private checkImports(document: TextDocument) {
1✔
26
    // We subscribe to multiple events which can be fired for any language, not just `Haskell` 
17✔
27
    if (document.languageId !== 'haskell') {
17✔
28
      return;
6✔
29
    }
6✔
30
    const imports = ImportDeclaration.getImports(document.getText());
11✔
31
    let messages = [];
11✔
32

11✔
33
    const aligned =
11✔
34
      imports.length === 0 || (
17✔
35
        (Configuration.shouldPadImports ||
5✔
36
          imports.some(imp => imp.qualified.trim() === "qualified")
5✔
37
        ) &&
5✔
38
        imports.every(imp => imp.qualified.length === " qualified ".length)
1✔
39
      ) || (
17✔
40
        !Configuration.shouldPadImports &&
5✔
41
        imports.every(imp => imp.qualified.trim() === "") &&
5✔
42
        imports.every(imp => imp.qualified.length === " ".length)
4✔
43
      );
17✔
44
    if (!aligned && Configuration.shouldAlignImports) {
17✔
45
      messages = ["not aligned"];
1✔
46
    }
1✔
47

11✔
48
    let pred = "";
11✔
49
    if (Configuration.shouldSortImports) {
11✔
50
      for (const imp of imports) {
11✔
51
        const curr = imp.module + (imp.importList || "");
8✔
52
        if (curr < pred || Configuration.shouldSortImportedElementLists && !imp.importElementsSorted) {
8✔
53
          messages.unshift("unsorted");
2✔
54
          break;
2✔
55
        }
2✔
56
        pred = curr;
6✔
57
      }
6✔
58
    }
11✔
59

11✔
60
    if (messages.length > 0) {
17✔
61
      const lastImport = imports[imports.length - 1];
2✔
62
      const range = new Range(
2✔
63
        document.positionAt(imports[0].offset),
2✔
64
        document.positionAt(lastImport.offset + lastImport.length));
2✔
65
      const message = `Imports are ${messages.join(" and ")}`;
2✔
66
      const diagnostic = new Diagnostic(range, message, DiagnosticSeverity.Hint);
2✔
67
      diagnostic.code = OrganizeImportProvider.diagnosticCode;
2✔
68
      this.diagnosticCollection.set(document.uri, [diagnostic]);
2✔
69
    }
2✔
70
    else {
9✔
71
      this.diagnosticCollection.delete(document.uri);
9✔
72
    }
9✔
73
  }
17✔
74

1✔
75
  public async provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): Promise<CodeAction[]> {
1✔
76
    let codeActions = [];
33✔
77
    const diagnostics = context.diagnostics.filter(d => d.code === OrganizeImportProvider.diagnosticCode);
33✔
78
    for (let diagnostic of diagnostics) {
33✔
79
      let title = "Organize imports";
3✔
80
      let codeAction = new CodeAction(title, CodeActionKind.QuickFix);
3✔
81
      codeAction.command = {
3✔
82
        title: title,
3✔
83
        command: OrganizeImportProvider.commandId,
3✔
84
        arguments: [
3✔
85
          document
3✔
86
        ]
3✔
87
      };
3✔
88
      codeAction.diagnostics = [diagnostic];
3✔
89
      codeActions.push(codeAction);
3✔
90
    }
3✔
91
    return codeActions;
33✔
92
  }
33✔
93

1✔
94
  private runCodeAction(document: TextDocument): Thenable<boolean> {
1✔
95
    const oldImports = ImportDeclaration.getImports(document.getText());
12✔
96
    let newImports = oldImports.map(i => i);
12✔
97
    if (Configuration.shouldSortImports) {
12✔
98
      if (Configuration.shouldSortImportedElementLists) {
12✔
99
        newImports.forEach(imp => imp.importElementsSorted || imp.sortImportElements());
12✔
100
      }
12✔
101
      newImports.sort((l, r) => {
12✔
102
        const ls = l.module + (l.importList || "");
32✔
103
        const rs = r.module + (r.importList || "");
32✔
104
        return ls < rs ? -1 : (ls === rs ? 0 : 1);
32!
105
      });
12✔
106
    }
12✔
107
    if (Configuration.shouldAlignImports) {
12✔
108
      newImports = OrganizeImportProvider.alignImports(newImports);
12✔
109
    }
12✔
110

12✔
111
    var edit = new WorkspaceEdit();
12✔
112
    for (let i = oldImports.length - 1; i >= 0; i--) {
12✔
113
      edit.delete(document.uri, oldImports[i].getRange(document));
40✔
114
      edit.insert(document.uri, oldImports[i].getRange(document).start, newImports[i].text);
40✔
115
    }
40✔
116
    return vscode.workspace.applyEdit(edit);
12✔
117
  }
12✔
118

1✔
119
  private ensureOrganized(event: TextDocumentWillSaveEvent) {
1✔
120
    if (Configuration.shouldOrganizeImportsOnSave) {
3!
121
      event.waitUntil(this.runCodeAction(event.document));
×
UNCOV
122
    }
×
123
  }
3✔
124

1✔
125
  public static ensureAligned(newImport: ImportDeclaration, existingImports: ImportDeclaration[]): ImportDeclaration {
1✔
126
    if (Configuration.shouldAlignImports) {
7✔
127
      let allImports = existingImports.map(imp => imp);
7✔
128
      allImports.unshift(newImport);
7✔
129
      OrganizeImportProvider.alignImports(allImports);
7✔
130
    }
7✔
131
    return newImport;
7✔
132
  }
7✔
133

1✔
134
  public static alignImports(imports: ImportDeclaration[]): ImportDeclaration[] {
1✔
135
    const isQualified = imp => imp.qualified.trim() === "qualified";
20✔
136

20✔
137
    return Configuration.shouldPadImports || imports.some(isQualified) ?
20✔
138
      imports.map(imp => {
5✔
139
        if (isQualified(imp)) {
16✔
140
          imp.qualified = " qualified ";
7✔
141
        }
7✔
142
        else {
9✔
143
          imp.qualified = "           ";
9✔
144
        }
9✔
145
        return imp;
16✔
146
      }) :
20✔
147
      imports.map(imp => {
15✔
148
        imp.qualified = " ";
46✔
149
        return imp;
46✔
150
      });
20✔
151
  }
20✔
152
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc