Skip to content

Commit 12bfe03

Browse files
committed
feat(harper.js): can export and import ignored lints
1 parent 487bc59 commit 12bfe03

File tree

6 files changed

+70
-7
lines changed

6 files changed

+70
-7
lines changed

harper-core/src/ignored_lints.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use std::hash::{DefaultHasher, Hash, Hasher};
22

33
use hashbrown::HashSet;
4+
use serde::{Deserialize, Serialize};
45

56
use crate::{linting::Lint, Document};
67

78
/// A structure that keeps track of lints that have been ignored by users.
8-
#[derive(Debug, Default)]
9+
#[derive(Debug, Default, Serialize, Deserialize)]
910
pub struct IgnoredLints {
1011
context_hashes: HashSet<u64>,
1112
}
@@ -15,6 +16,11 @@ impl IgnoredLints {
1516
Self::default()
1617
}
1718

19+
/// Move entries from another instance to this one.
20+
pub fn append(&mut self, other: Self) {
21+
self.context_hashes.extend(other.context_hashes)
22+
}
23+
1824
fn hash_lint_context(&self, lint: &Lint, document: &Document) -> u64 {
1925
let problem_tokens = document.token_indices_intersecting(lint.span);
2026
let prequel_tokens = lint

harper-wasm/src/lib.rs

+14
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,20 @@ impl Linter {
160160
.map(|l| Lint::new(l, source.to_vec(), language))
161161
.collect()
162162
}
163+
164+
/// Export the linter's ignored lints as a privacy-respecting JSON list of hashes.
165+
pub fn export_ignored_lints(&self) -> String {
166+
serde_json::to_string(&self.ignored_lints).unwrap()
167+
}
168+
169+
/// Import into the linter's ignored lints from a privacy-respecting JSON list of hashes.
170+
pub fn import_ignored_lints(&mut self, json: String) -> Result<(), String> {
171+
let list: IgnoredLints = serde_json::from_str(&json).map_err(|err| err.to_string())?;
172+
173+
self.ignored_lints.append(list);
174+
175+
Ok(())
176+
}
163177
}
164178

165179
impl Default for Linter {

packages/harper.js/src/Linter.test.ts

+22-6
Original file line numberDiff line numberDiff line change
@@ -137,21 +137,37 @@ for (const [linterName, Linter] of Object.entries(linters)) {
137137
const source = 'This is an test.';
138138

139139
const firstRound = await linter.lint(source);
140-
for (const lint of firstRound) {
141-
console.log(lint.message());
142-
}
143140

144141
expect(firstRound.length).toBeGreaterThanOrEqual(1);
145142

146143
await linter.ignoreLint(firstRound[0]);
147144

148145
const secondRound = await linter.lint(source);
149146

150-
for (const lint of secondRound) {
151-
console.log(lint.message());
147+
expect(secondRound.length).toBeLessThan(firstRound.length);
148+
});
149+
150+
test(`${linterName} can reimport ignored lints.`, async () => {
151+
const source = 'This is an test of xporting lints.';
152+
153+
const firstLinter = new Linter();
154+
155+
const firstLints = await firstLinter.lint(source);
156+
157+
for (const lint of firstLints) {
158+
await firstLinter.ignoreLint(lint);
152159
}
153160

154-
expect(secondRound.length).toBeLessThan(firstRound.length);
161+
const exported = await firstLinter.exportIgnoredLints();
162+
163+
/// Create a new instance and reimport the lints.
164+
const secondLinter = new Linter();
165+
await secondLinter.importIgnoredLints(exported);
166+
167+
const secondLints = await secondLinter.lint(source);
168+
169+
expect(firstLints.length).toBeGreaterThan(secondLints.length);
170+
expect(secondLints.length).toBe(0);
155171
});
156172
}
157173

packages/harper.js/src/Linter.ts

+7
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,11 @@ export default interface Linter {
5353

5454
/** Ignore future instances of a lint from a previous linting run in future invocations. */
5555
ignoreLint(lint: Lint): Promise<void>;
56+
57+
/** Export the ignored lints to a JSON list of privacy-respecting hashes. */
58+
exportIgnoredLints(): Promise<string>;
59+
60+
/** Import ignored lints from a JSON list to the linter.
61+
* This function appends to the existing lints, if any. */
62+
importIgnoredLints(json: string): Promise<void>;
5663
}

packages/harper.js/src/LocalLinter.ts

+12
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,16 @@ export default class LocalLinter implements Linter {
103103

104104
this.inner!.ignore_lint(lint);
105105
}
106+
107+
async exportIgnoredLints(): Promise<string> {
108+
await this.initialize();
109+
110+
return this.inner!.export_ignored_lints();
111+
}
112+
113+
async importIgnoredLints(json: string): Promise<void> {
114+
await this.initialize();
115+
116+
return this.inner!.import_ignored_lints(json);
117+
}
106118
}

packages/harper.js/src/WorkerLinter/index.ts

+8
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ export default class WorkerLinter implements Linter {
117117
return this.rpc('ignoreLint', [lint]);
118118
}
119119

120+
async exportIgnoredLints(): Promise<string> {
121+
return this.rpc('exportIgnoredLints', []);
122+
}
123+
124+
async importIgnoredLints(json: string): Promise<void> {
125+
return this.rpc('importIgnoredLints', [json]);
126+
}
127+
120128
/** Run a procedure on the remote worker. */
121129
private async rpc(procName: string, args: any[]): Promise<any> {
122130
const promise = new Promise((resolve, reject) => {

0 commit comments

Comments
 (0)