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

EduardSergeev / vscode-haskutil / 9630082887

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

push

github

web-flow
Sort imported element lists on `Organize imports` (#89)

- Changed:
  - `Organize imports` can now also sort imported element lists (closes: #86):
    When sorting import declaration if new configuration options `"haskutil.sortImportedElementLists"` is set to `true` (default value)
  - An option to place operators at the end of imported element lists (closes: #88):
    When inserting new elements via `Add import` or sorting imported elements when via `Organize imports` operators can now be optionally placed after other type of elements, i.e. after functions, constructors or types.  
    This behavior is controlled via new configuration setting `"haskutil.placeOperatorsAfterFunctions"` which is set to `false` by
default
  - Switch to [c8](https://github.com/bcoe/c8) for code coverage (fixes: #87)

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.78 hits per line

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

97.1
/src/features/importProvider/importProviderBase.ts
1
import * as vscode from 'vscode';
1✔
2
import { Disposable, TextDocument, Range, WorkspaceEdit } from 'vscode';
1✔
3
import ExtensionProvider from '../extensionProvider';
1✔
4
import ImportDeclaration from './importDeclaration';
1✔
5
import OrganizeImportProvider from '../organizeImportProvider';
1✔
6
import Configuration from '../../configuration';
1✔
7

1✔
8

1✔
9
export interface SearchResult {
1✔
10
  package: string;
1✔
11
  module: string;
1✔
12
  result: string;
1✔
13
}
1✔
14

1✔
15
export default class ImportProviderBase {
1✔
16
  private static modulePattern = /^module(.|\n)+?where/m;
1✔
17
  private hoogleSearch: (name: string, resultCallback: HoogleSearchCallback) => void;
1✔
18

1✔
19
  constructor(protected commandId: string) {
1✔
20
  }
2✔
21

1✔
22
  public activate(subscriptions: Disposable[]) {
1✔
23
    const command = vscode.commands.registerCommand(this.commandId, this.runCodeAction, this);
2✔
24
    subscriptions.push(command);
2✔
25

2✔
26
    const hoogle = vscode.extensions.getExtension('jcanero.hoogle-vscode');
2✔
27
    const hoogleApi = hoogle.exports;
2✔
28
    this.hoogleSearch = hoogleApi.search;
2✔
29
  }
2✔
30

1✔
31
  protected search(name: string): Promise<SearchResult[]> {
1✔
32
    const result = new Promise<SearchResult[]>(resolve => {
15✔
33
      this.hoogleSearch(name, searchResult => {
15✔
34
        resolve(searchResult.results
15✔
35
          .filter(result => {
15✔
36
            if (!result.isModule() && !result.isPackage()) {
75✔
37
              const r = this.decodeHtmlEntity(result.getQueryResult());
74✔
38
              const i = r.indexOf(name);
74✔
39
              const j = i + name.length;
74✔
40
              return (i >= 0) &&
74✔
41
                (i === 0 || r[i - 1] === " " || r[i - 1] === '(') &&
74✔
42
                (j === r.length || r[j] === " " || r[j] === ")");
74✔
43
            }
74✔
44
            else {
1✔
45
              return false;
1✔
46
            }
1✔
47
          }).map(result => {
15✔
48
            return {
59✔
49
              package: result.getPackageName(),
59✔
50
              module: result.getModuleName().replace(/-/g, '.'),
59✔
51
              result: result.getQueryResult()
59✔
52
            };
59✔
53
          })
15✔
54
        );
15✔
55
      });
15✔
56
    });
15✔
57
    return result;
15✔
58
  }
15✔
59

1✔
60
  private decodeHtmlEntity(str: string): string {
1✔
61
    return str.replace(/&#(\d+);/g, (_, dec) =>
74✔
62
      String.fromCharCode(dec));
74✔
63
  }
74✔
64

1✔
65
  private async runCodeAction(document: TextDocument, moduleName: string, options: { qualified?: Boolean, alias?: string, elementName?: string } = {}): Promise<void> {
1✔
66
    function afterMatch(offset) {
10✔
67
      const position = document.positionAt(offset);
5✔
68
      return document.offsetAt(position.with(position.line + 1, 0));
5✔
69
    }
5✔
70

10✔
71
    const text = document.getText();
10✔
72
    let position = 0;
10✔
73

10✔
74
    for (let match, pattern = ExtensionProvider.extensionPattern; match = pattern.exec(text);) {
10!
75
      position = afterMatch(match.index + match[0].length);
×
UNCOV
76
    }
×
77

10✔
78
    const match = ImportProviderBase.modulePattern.exec(text);
10✔
79
    if (match !== null) {
10!
80
      position = afterMatch(match.index + match[0].length);
×
UNCOV
81
    }
×
82

10✔
83
    const edit = new WorkspaceEdit();
10✔
84

10✔
85
    const oldImports = ImportDeclaration.getImports(text);
10✔
86
    const oldImport =
10✔
87
      oldImports.find(decl => decl.module === moduleName && decl.importList !== null) ||
10✔
88
      oldImports.find(decl => decl.module === moduleName);
10✔
89
    if (oldImport && options.elementName) {
10✔
90
      oldImport.addImportElement(options.elementName);
3✔
91
      const oldRange = new Range(
3✔
92
        document.positionAt(oldImport.offset),
3✔
93
        document.positionAt(oldImport.offset + oldImport.length));
3✔
94
      edit.replace(document.uri, oldRange, oldImport.text);
3✔
95
    }
3✔
96
    else {
7✔
97
      for (const importInfo of oldImports) {
7✔
98
        if (importInfo.module + (importInfo.importList || "") > moduleName) {
9✔
99
          position = importInfo.offset;
4✔
100
          break;
4✔
101
        }
4✔
102
        position = afterMatch(importInfo.offset + importInfo.length);
5✔
103
      }
5✔
104
      const importDeclaration = new ImportDeclaration(moduleName);
7✔
105
      if (options.qualified) {
7✔
106
        importDeclaration.qualified = " qualified ";
2✔
107
        if (options.alias) {
2✔
108
          importDeclaration.alias = options.alias;
1✔
109
        }
1✔
110
      }
2✔
111
      if (options.elementName) {
7✔
112
        importDeclaration.addImportElement(options.elementName);
4✔
113
      }
4✔
114

7✔
115
      // Align import if necessary
7✔
116
      OrganizeImportProvider.ensureAligned(importDeclaration, oldImports);
7✔
117

7✔
118
      edit.insert(document.uri, document.positionAt(position), importDeclaration.text + "\n");
7✔
119
    }
7✔
120

10✔
121
    await vscode.workspace.applyEdit(edit);
10✔
122
    if(Configuration.shouldOrganiseImportsOnInsert) {
10✔
123
      await vscode.commands.executeCommand(OrganizeImportProvider.commandId, document);
10✔
124
    }
10✔
125
  }
10✔
126
}
1✔
127

1✔
128
interface HoogleSearchCallback {
1✔
129
  (result: { results: HoogleResult[] }): void;
1✔
130
}
1✔
131

1✔
132
interface HoogleResult {
1✔
133
  isModule(): boolean;
1✔
134
  isPackage(): boolean;
1✔
135
  getPackageName(): string;
1✔
136
  getModuleName(): string;
1✔
137
  getQueryResult(): string;
1✔
138
}
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