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

EduardSergeev / vscode-haskutil / 9630010233

23 Jun 2024 02:10AM UTC coverage: 97.326% (+1.3%) from 96.059%
9630010233

Pull #89

github

web-flow
Merge 42bd3df9c 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.57 hits per line

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

98.71
/src/features/organizeExtensionProvider.ts
1
'use strict';
1✔
2

1✔
3
import { CodeActionProvider, Disposable, TextDocument, Range, CodeActionContext, CancellationToken, CodeAction, WorkspaceEdit, CodeActionKind, Diagnostic, DiagnosticSeverity, WorkspaceConfiguration, TextDocumentWillSaveEvent } from 'vscode';
1✔
4
import * as vscode from 'vscode';
1✔
5
import ExtensionDeclaration from './extensionProvider/extensionDeclaration';
1✔
6
import Configuration from '../configuration';
1✔
7

1✔
8

1✔
9
export default class OrganizeExtensionProvider implements CodeActionProvider {
1✔
10
  public static commandId: string = 'haskell.organizeExtensions';
1✔
11
  private diagnosticCollection: vscode.DiagnosticCollection;
1✔
12
  private static diagnosticCode: string = "haskutil.unorganizedExtensions";
1✔
13

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

1✔
18
    this.diagnosticCollection = vscode.languages.createDiagnosticCollection();
1✔
19
    subscriptions.push(this.diagnosticCollection);
1✔
20
    vscode.workspace.onDidOpenTextDocument(this.checkExtensions, this, subscriptions);
1✔
21
    vscode.workspace.onDidCloseTextDocument(doc => this.diagnosticCollection.delete(doc.uri), null, subscriptions);
1✔
22
    vscode.workspace.onDidSaveTextDocument(this.checkExtensions, this, subscriptions);
1✔
23
    vscode.workspace.onWillSaveTextDocument(this.ensureOrganized, this, subscriptions);
1✔
24
    vscode.workspace.textDocuments.forEach(this.checkExtensions, this);
1✔
25
  }
1✔
26

1✔
27
  private checkExtensions(document: TextDocument) {
1✔
28
    const extensions = ExtensionDeclaration.getExtensions(document.getText());
17✔
29

17✔
30
    let unorganized =
17✔
31
      extensions.some(extension => extension.extensionNames.length > 1);
17✔
32

17✔
33
    const aligned =
17✔
34
      extensions.length === 0 ||
17✔
35
      extensions.every(extension => extension.extensions.length === extensions[0].extensions.length) &&
2✔
36
      Math.max(...extensions.map(extension => extension.extensions.trimEnd().length + 1)) === extensions[0].extensions.length;
17✔
37
    unorganized = unorganized || Configuration.shouldAlignExtensions && !aligned;
17✔
38

17✔
39
    let pred = "";
17✔
40
    if (Configuration.shouldSortExtensions) {
17✔
41
      for (const extension of extensions) {
17✔
42
        const curr = extension.extensions;
3✔
43
        if (curr < pred) {
3✔
44
          unorganized = true;
1✔
45
          break;
1✔
46
        }
1✔
47
        pred = curr;
2✔
48
      }
2✔
49
    }
17✔
50

17✔
51
    if (unorganized) {
17✔
52
      const lastExtension = extensions[extensions.length - 1];
1✔
53
      const range = new Range(
1✔
54
        document.positionAt(extensions[0].offset),
1✔
55
        document.positionAt(lastExtension.offset + lastExtension.length));
1✔
56
      const message = `Extension are unorganised`;
1✔
57
      const diagnostic = new Diagnostic(range, message, DiagnosticSeverity.Hint);
1✔
58
      diagnostic.code = OrganizeExtensionProvider.diagnosticCode;
1✔
59
      this.diagnosticCollection.set(document.uri, [diagnostic]);
1✔
60
    }
1✔
61
    else {
16✔
62
      this.diagnosticCollection.delete(document.uri);
16✔
63
    }
16✔
64
  }
17✔
65

1✔
66
  public async provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): Promise<CodeAction[]> {
1✔
67
    let codeActions = [];
27✔
68
    const diagnostics = context.diagnostics.filter(d => d.code === OrganizeExtensionProvider.diagnosticCode);
27✔
69
    for (let diagnostic of diagnostics) {
27✔
70
      let title = "Organize extensions";
1✔
71
      let codeAction = new CodeAction(title, CodeActionKind.QuickFix);
1✔
72
      codeAction.command = {
1✔
73
        title: title,
1✔
74
        command: OrganizeExtensionProvider.commandId,
1✔
75
        arguments: [
1✔
76
          document
1✔
77
        ]
1✔
78
      };
1✔
79
      codeAction.diagnostics = [diagnostic];
1✔
80
      codeActions.push(codeAction);
1✔
81
    }
1✔
82
    return codeActions;
27✔
83
  }
27✔
84

1✔
85
  private async runCodeAction(document: TextDocument) {
1✔
86
    if (Configuration.shouldSplitExtensions) {
2✔
87
      await this.splitExtensions(document);
2✔
88
    }
2✔
89
    if (Configuration.shouldAlignExtensions) {
2✔
90
      await this.alignExtensions(document);
2✔
91
    }
2✔
92
    if (Configuration.shouldSortExtensions) {
2✔
93
      await this.sortExtensions(document);
2✔
94
    }
2✔
95
  }
2✔
96

1✔
97
  private ensureOrganized(event: TextDocumentWillSaveEvent) {
1✔
98
    if (Configuration.shouldOrganizeExtensionsOnSave) {
3!
99
      event.waitUntil(this.runCodeAction(event.document));
×
UNCOV
100
    }
×
101
  }
3✔
102

1✔
103
  private async splitExtensions(document: TextDocument) {
1✔
104
    for (const extension of ExtensionDeclaration.getExtensions(document.getText()).reverse()) {
2✔
105
      await this.splitExtension(extension, document);
3✔
106
    }
3✔
107
  }
2✔
108

1✔
109
  private async splitExtension(extension: ExtensionDeclaration, document: TextDocument) {
1✔
110
    if (extension.extensionNames.length > 1) {
3✔
111
      const edit = new WorkspaceEdit();
1✔
112
      const range = extension.getRange(document);
1✔
113
      edit.delete(document.uri, range.with({ end: range.end.with(range.end.line + 1, 0) }));
1✔
114

1✔
115
      const extensions = extension.extensionNames.map(name =>
1✔
116
        new ExtensionDeclaration(extension.header, `${name} `));
1✔
117
      extensions.sort((l, r) => r.text.localeCompare(l.text));
1✔
118
      for (const newExtension of extensions) {
1✔
119
        edit.insert(document.uri, range.start, newExtension.text + "\n");
3✔
120
      }
3✔
121
      await vscode.workspace.applyEdit(edit);
1✔
122
    }
1✔
123
  }
3✔
124

1✔
125
  private async alignExtensions(document: TextDocument) {
1✔
126
    const oldExtensions = ExtensionDeclaration.getExtensions(document.getText());
2✔
127
    const length = Math.max(...oldExtensions.map(extension => extension.extensions.trimEnd().length));
2✔
128
    const newExtensions = oldExtensions.map(extension => {
2✔
129
      const extensionsTrimmed = extension.extensions.trimEnd();
5✔
130
      return new ExtensionDeclaration(
5✔
131
        extension.header,
5✔
132
        extensionsTrimmed.concat(" ".repeat(length - extensionsTrimmed.length + 1)));
5✔
133
    });
2✔
134

2✔
135
    var edit = new WorkspaceEdit();
2✔
136
    for (let i = oldExtensions.length - 1; i >= 0; i--) {
2✔
137
      edit.delete(document.uri, oldExtensions[i].getRange(document));
5✔
138
      edit.insert(document.uri, oldExtensions[i].getRange(document).start, newExtensions[i].text);
5✔
139
    }
5✔
140
    await vscode.workspace.applyEdit(edit);
2✔
141
  }
2✔
142

1✔
143
  private async sortExtensions(document: TextDocument) {
1✔
144
    const oldExtensions = ExtensionDeclaration.getExtensions(document.getText());
2✔
145
    let newExtensions = oldExtensions.map(i => i);
2✔
146
    newExtensions.sort((l, r) => l.text.localeCompare(r.text));
2✔
147

2✔
148
    var edit = new WorkspaceEdit();
2✔
149
    for (let i = oldExtensions.length - 1; i >= 0; i--) {
2✔
150
      edit.delete(document.uri, oldExtensions[i].getRange(document));
5✔
151
      edit.insert(document.uri, oldExtensions[i].getRange(document).start, newExtensions[i].text);
5✔
152
    }
5✔
153
    await vscode.workspace.applyEdit(edit);
2✔
154
  }
2✔
155
}
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