-
-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: refactor syntax checker (#431)
* feat: refactor analyzer logic * fix: remove log
- Loading branch information
Showing
16 changed files
with
339 additions
and
365 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 1 addition & 2 deletions
3
web/src/components/features/workspace/CodeEditor/autocomplete/cache.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
web/src/components/features/workspace/CodeEditor/autocomplete/utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const stripSlash = (str: string) => (str[0] === '/' ? str.slice(1) : str) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
web/src/components/features/workspace/CodeEditor/syntaxcheck/checker.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import * as monaco from 'monaco-editor' | ||
import { newMarkerAction, type StateDispatch } from '~/store' | ||
import { type AnalyzerWorker, spawnAnalyzerWorker } from '~/workers/analyzer' | ||
import { LanguageID } from '../grammar' | ||
import { getTimeNowUsageMarkers } from './time' | ||
import { stripSlash } from '../autocomplete/utils' | ||
|
||
type EditorInstance = monaco.editor.IStandaloneCodeEditor | null | undefined | ||
type TimeoutId = ReturnType<typeof setTimeout> | ||
export interface EnvironmentContext { | ||
isServerEnvironment?: boolean | ||
} | ||
|
||
const debounceInterval = 500 | ||
|
||
export class GoSyntaxChecker implements monaco.IDisposable { | ||
private readonly disposer: monaco.IDisposable | ||
private readonly worker: AnalyzerWorker | ||
private readonly timeoutId?: TimeoutId | ||
|
||
constructor(private readonly dispatcher: StateDispatch) { | ||
const [worker, disposer] = spawnAnalyzerWorker() | ||
this.worker = worker | ||
this.disposer = disposer | ||
} | ||
|
||
requestModelMarkers( | ||
model: monaco.editor.ITextModel | null | undefined, | ||
editor: EditorInstance, | ||
ctx: EnvironmentContext, | ||
) { | ||
if (!editor || !model || model.getLanguageId() !== LanguageID.Go) { | ||
return | ||
} | ||
|
||
this.cancelScheduledChecks() | ||
setTimeout(() => { | ||
void this.checkModel(model, editor.getId(), ctx) | ||
}, debounceInterval) | ||
} | ||
|
||
cancelScheduledChecks() { | ||
if (this.timeoutId) { | ||
clearTimeout(this.timeoutId) | ||
} | ||
} | ||
|
||
private async checkModel(model: monaco.editor.ITextModel, editorId: string, ctx: EnvironmentContext) { | ||
// Code analysis contains 2 steps that run on different conditions: | ||
// 1. Add warnings to `time.Now` calls if code runs on server. | ||
// 2. Run Go worker if it's available and check for errors | ||
const fileName = model.uri.fsPath | ||
const markers: monaco.editor.IMarkerData[] = ctx.isServerEnvironment ? getTimeNowUsageMarkers(model) : [] | ||
|
||
try { | ||
const response = await this.worker.checkSyntaxErrors({ | ||
fileName, | ||
contents: model.getValue(), | ||
}) | ||
|
||
if (response.fileName === fileName && response.markers) { | ||
markers.push(...response.markers) | ||
} | ||
} catch (err) { | ||
console.error('failed to perform syntax check', err) | ||
} | ||
|
||
monaco.editor.setModelMarkers(model, editorId, markers) | ||
this.dispatcher(newMarkerAction(stripSlash(fileName), markers)) | ||
} | ||
|
||
dispose() { | ||
this.disposer.dispose() | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
web/src/components/features/workspace/CodeEditor/syntaxcheck/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './checker' |
45 changes: 45 additions & 0 deletions
45
web/src/components/features/workspace/CodeEditor/syntaxcheck/time.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import * as monaco from 'monaco-editor' | ||
import environment from '~/environment' | ||
|
||
const segmentLength = 'time.Now()'.length | ||
const issueUrl = monaco.Uri.parse(`${environment.urls.github}/issues/104`) | ||
const timeNowUsageWarning = | ||
'Warning: `time.Now()` will always return fake time. ' + | ||
'Change current environment to WebAssembly to use real date and time.' | ||
|
||
/** | ||
* Checks if passed source code contains `time.Now()` calls and returns | ||
* a list of warning markers for those calls. | ||
* | ||
* Returns empty array if no calls found. | ||
*/ | ||
export const getTimeNowUsageMarkers = (model: monaco.editor.ITextModel): monaco.editor.IMarkerData[] => { | ||
const code = model.getValue() | ||
const regex = /\W(time\.Now\(\))/gm | ||
const matches: number[] = [] | ||
let match: RegExpExecArray | null | ||
while ((match = regex.exec(code))) { | ||
matches.push(match.index) | ||
} | ||
|
||
if (matches.length === 0) { | ||
return [] | ||
} | ||
|
||
return matches.map((index) => { | ||
// Skip extra character or white-space before expression | ||
const { lineNumber, column } = model.getPositionAt(index + 1) | ||
return { | ||
code: { | ||
value: 'More information', | ||
target: issueUrl, | ||
}, | ||
severity: monaco.MarkerSeverity.Warning, | ||
message: timeNowUsageWarning, | ||
startLineNumber: lineNumber, | ||
endLineNumber: lineNumber, | ||
startColumn: column, | ||
endColumn: column + segmentLength, | ||
} | ||
}) | ||
} |
Oops, something went wrong.