more error handling

pull/2917/head
filip mertens 2 years ago
parent 58390a8a49
commit 6b115f5550
  1. 78
      apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts
  2. 6
      apps/remix-ide/src/app/plugins/parser/services/code-parser-imports.ts
  3. 7
      libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts
  4. 31
      libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt
  5. 7
      libs/remix-ui/editor/src/lib/providers/completionProvider.ts
  6. 18
      libs/remix-ui/editor/src/lib/providers/definitionProvider.ts
  7. 2
      libs/remix-ui/search/src/index.ts
  8. 2
      package.json

@ -8,6 +8,7 @@ import { fileDecoration, fileDecorationType } from '@remix-ui/file-decorators'
import { sourceMappingDecoder } from '@remix-project/remix-debug' import { sourceMappingDecoder } from '@remix-project/remix-debug'
import { CompilerRetriggerMode } from '@remix-project/remix-solidity-ts'; import { CompilerRetriggerMode } from '@remix-project/remix-solidity-ts';
import { MarkerSeverity } from 'monaco-editor'; import { MarkerSeverity } from 'monaco-editor';
import { findLinesInStringWithMatch, SearchResultLine } from '@remix-ui/search'
type errorMarker = { type errorMarker = {
message: string message: string
@ -39,39 +40,49 @@ export default class CodeParserCompiler {
init() { init() {
this.onAstFinished = async (success, data: CompilationResult, source: CompilationSource, input: any, version) => { this.onAstFinished = async (success, data: CompilationResult, source: CompilationSource, input: any, version) => {
//console.log('onAstFinished', success, data, source, input, version)
this.plugin.call('editor', 'clearAnnotations') this.plugin.call('editor', 'clearAnnotations')
this.errorState = true this.errorState = true
const result = new CompilerAbstract('soljson', data, source, input) const result = new CompilerAbstract('soljson', data, source, input)
let allErrors: errorMarker[] = [] let allErrors: errorMarker[] = []
if (data.errors) { if (data.errors || data.error) {
const sources = result.getSourceCode().sources const file = await this.plugin.call('fileManager', 'getCurrentFile')
const currentFileContent = await this.plugin.call('fileManager', 'readFile', file)
const sources = result.getSourceCode().sources || []
if (data.error) {
if (data.error.formattedMessage) {
// mark this file as error
const errorMarker = await this.createErrorMarker(data.error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } })
allErrors = [...allErrors, errorMarker]
}
} else {
for (const error of data.errors) { for (const error of data.errors) {
//console.log(error) if (!error.sourceLocation) {
// mark this file as error
const errorMarker = await this.createErrorMarker(error, file, { start: { line: 0, column: 0 }, end: { line: 0, column: 100 } })
allErrors = [...allErrors, errorMarker]
} else {
const lineBreaks = sourceMappingDecoder.getLinebreakPositions(sources[error.sourceLocation.file].content) const lineBreaks = sourceMappingDecoder.getLinebreakPositions(sources[error.sourceLocation.file].content)
const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn({ const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn({
start: error.sourceLocation.start, start: error.sourceLocation.start,
length: error.sourceLocation.end - error.sourceLocation.start length: error.sourceLocation.end - error.sourceLocation.start
}, lineBreaks) }, lineBreaks)
const filePath = error.sourceLocation.file const filePath = error.sourceLocation.file
const fileTarget = await this.plugin.call('fileManager', 'getUrlFromPath', filePath)
allErrors = [...allErrors, { const importFilePositions = await this.getPositionForImportErrors(fileTarget.file, currentFileContent)
message: error.formattedMessage, for (const importFilePosition of importFilePositions) {
severity: error.severity === 'error' ? MarkerSeverity.Error : MarkerSeverity.Warning, for (const line of importFilePosition.lines) {
position: { allErrors = [...allErrors, await this.createErrorMarker(error, file, line.position)]
start: { }
line: ((lineColumn.start && lineColumn.start.line) || 0) + 1, }
column: ((lineColumn.start && lineColumn.start.column) || 0) + 1
}, allErrors = [...allErrors, await this.createErrorMarker(error, filePath, lineColumn)]
end: {
line: ((lineColumn.end && lineColumn.end.line) || 0) + 1,
column: ((lineColumn.end && lineColumn.end.column) || 0) + 1
} }
} }
, file: filePath
}]
} }
const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors') const displayErrors = await this.plugin.call('config', 'getAppParameter', 'display-errors')
if (displayErrors) await this.plugin.call('editor', 'addErrorMarker', allErrors) if (displayErrors) await this.plugin.call('editor', 'addErrorMarker', allErrors)
this.addDecorators(allErrors, sources) this.addDecorators(allErrors, sources)
@ -179,8 +190,9 @@ export default class CodeParserCompiler {
const decorators: fileDecoration[] = [] const decorators: fileDecoration[] = []
for (const fileName in sortedErrorsPerFiles) { for (const fileName in sortedErrorsPerFiles) {
const errors = sortedErrorsPerFiles[fileName] const errors = sortedErrorsPerFiles[fileName]
const fileTarget = await this.plugin.call('fileManager', 'getPathFromUrl', fileName)
const decorator: fileDecoration = { const decorator: fileDecoration = {
path: fileName, path: fileTarget.file,
isDirectory: false, isDirectory: false,
fileStateType: errors[0].severity == MarkerSeverity.Error ? fileDecorationType.Error : fileDecorationType.Warning, fileStateType: errors[0].severity == MarkerSeverity.Error ? fileDecorationType.Error : fileDecorationType.Warning,
fileStateLabelClass: errors[0].severity == MarkerSeverity.Error ? 'text-danger' : 'text-warning', fileStateLabelClass: errors[0].severity == MarkerSeverity.Error ? 'text-danger' : 'text-warning',
@ -194,8 +206,9 @@ export default class CodeParserCompiler {
decorators.push(decorator) decorators.push(decorator)
} }
for (const fileName of filesWithOutErrors) { for (const fileName of filesWithOutErrors) {
const fileTarget = await this.plugin.call('fileManager', 'getPathFromUrl', fileName)
const decorator: fileDecoration = { const decorator: fileDecoration = {
path: fileName, path: fileTarget.file,
isDirectory: false, isDirectory: false,
fileStateType: fileDecorationType.None, fileStateType: fileDecorationType.None,
fileStateLabelClass: '', fileStateLabelClass: '',
@ -212,6 +225,24 @@ export default class CodeParserCompiler {
} }
async createErrorMarker(error: any, filePath: string, lineColumn): Promise<errorMarker> {
return {
message: error.formattedMessage,
severity: error.severity === 'error' ? MarkerSeverity.Error : MarkerSeverity.Warning,
position: {
start: {
line: ((lineColumn.start && lineColumn.start.line) || 0) + 1,
column: ((lineColumn.start && lineColumn.start.column) || 0) + 1
},
end: {
line: ((lineColumn.end && lineColumn.end.line) || 0) + 1,
column: ((lineColumn.end && lineColumn.end.column) || 0) + 1
}
}
, file: filePath
}
}
async clearDecorators(sources: any) { async clearDecorators(sources: any) {
const decorators: fileDecoration[] = [] const decorators: fileDecoration[] = []
if (!sources) return if (!sources) return
@ -234,4 +265,13 @@ export default class CodeParserCompiler {
await this.plugin.call('fileDecorator', 'setFileDecorators', decorators) await this.plugin.call('fileDecorator', 'setFileDecorators', decorators)
} }
async getPositionForImportErrors(importedFileName: string, text: string) {
const re = new RegExp(importedFileName, 'gi')
const result: SearchResultLine[] = findLinesInStringWithMatch(
text,
re
)
return result
}
} }

@ -23,8 +23,6 @@ export default class CodeParserImports {
async init() { async init() {
// @ts-ignore // @ts-ignore
const txt = await import('raw-loader!libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt') const txt = await import('raw-loader!libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
this.data.modules = txt.default.split('\n') this.data.modules = txt.default.split('\n')
.filter(x => x !== '') .filter(x => x !== '')
.map(x => x.replace('./node_modules/', '')) .map(x => x.replace('./node_modules/', ''))
@ -38,13 +36,11 @@ export default class CodeParserImports {
// get unique first words of the values in the array // get unique first words of the values in the array
this.data.packages = [...new Set(this.data.modules.map(x => x.split('/')[0]))] this.data.packages = [...new Set(this.data.modules.map(x => x.split('/')[0]))]
console.log(this.data)
} }
setFileTree = async () => { setFileTree = async () => {
this.data.files = await this.getDirectory('/') this.data.files = await this.getDirectory('/')
this.data.files = this.data.files.filter(x => x.endsWith('.sol') && !x.startsWith('.deps')) this.data.files = this.data.files.filter(x => x.endsWith('.sol') && !x.startsWith('.deps') && !x.startsWith('.git'))
console.log(this.data)
} }
getDirectory = async (dir: string) => { getDirectory = async (dir: string) => {

@ -183,13 +183,6 @@ export function getCompletionSnippets(range: IRange, monaco): monaco.languages.C
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range range
}, },
{
label: 'import',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: 'import "${1:library}";',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range
},
{ {
label: 'SPDX-License-Identifier', label: 'SPDX-License-Identifier',
kind: monaco.languages.CompletionItemKind.Snippet, kind: monaco.languages.CompletionItemKind.Snippet,

@ -1,3 +1,34 @@
./node_modules/@hq20/contracts/contracts/access/AccessControlBasic.sol
./node_modules/@hq20/contracts/contracts/access/Administered.sol
./node_modules/@hq20/contracts/contracts/access/AuthorizedAccess.sol
./node_modules/@hq20/contracts/contracts/access/Community.sol
./node_modules/@hq20/contracts/contracts/access/Democracy.sol
./node_modules/@hq20/contracts/contracts/access/Hierarchy.sol
./node_modules/@hq20/contracts/contracts/classifieds/Classifieds.sol
./node_modules/@hq20/contracts/contracts/dao/DAO.sol
./node_modules/@hq20/contracts/contracts/dao/VentureEth.sol
./node_modules/@hq20/contracts/contracts/energy/EnergyMarket.sol
./node_modules/@hq20/contracts/contracts/exchange/UniswapExchange.sol
./node_modules/@hq20/contracts/contracts/exchange/UniswapFactory.sol
./node_modules/@hq20/contracts/contracts/introspection/erc165/ERC20Whitelisted.sol
./node_modules/@hq20/contracts/contracts/introspection/erc165/IWhitelist.sol
./node_modules/@hq20/contracts/contracts/introspection/erc165/WhitelistERC165.sol
./node_modules/@hq20/contracts/contracts/introspection/erc165/WhitelistInterfaceId.sol
./node_modules/@hq20/contracts/contracts/issuance/Issuance.sol
./node_modules/@hq20/contracts/contracts/issuance/IssuanceEth.sol
./node_modules/@hq20/contracts/contracts/lists/DoubleLinkedList.sol
./node_modules/@hq20/contracts/contracts/lists/LinkedList.sol
./node_modules/@hq20/contracts/contracts/math/DecimalMath.sol
./node_modules/@hq20/contracts/contracts/state/StateMachine.sol
./node_modules/@hq20/contracts/contracts/token/ERC20DividendableEth.sol
./node_modules/@hq20/contracts/contracts/token/ERC20Mintable.sol
./node_modules/@hq20/contracts/contracts/token/ERC721Mintable.sol
./node_modules/@hq20/contracts/contracts/token/IERC20Detailed.sol
./node_modules/@hq20/contracts/contracts/token/IERC20Mintable.sol
./node_modules/@hq20/contracts/contracts/utils/SafeCast.sol
./node_modules/@hq20/contracts/contracts/voting/Democratic.sol
./node_modules/@hq20/contracts/contracts/voting/OneManOneVote.sol
./node_modules/@hq20/contracts/contracts/voting/OneTokenOneVote.sol
./node_modules/@openzeppelin/contracts/access/AccessControl.sol ./node_modules/@openzeppelin/contracts/access/AccessControl.sol
./node_modules/@openzeppelin/contracts/access/AccessControlCrossChain.sol ./node_modules/@openzeppelin/contracts/access/AccessControlCrossChain.sol
./node_modules/@openzeppelin/contracts/access/AccessControlEnumerable.sol ./node_modules/@openzeppelin/contracts/access/AccessControlEnumerable.sol

@ -28,16 +28,16 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
startColumn: word.startColumn, startColumn: word.startColumn,
endColumn: word.endColumn endColumn: word.endColumn
}; };
console.log(word)
const line = model.getLineContent(position.lineNumber) const line = model.getLineContent(position.lineNumber)
let nodes: AstNode[] = [] let nodes: AstNode[] = []
let suggestions: monaco.languages.CompletionItem[] = [] let suggestions: monaco.languages.CompletionItem[] = []
console.log('context', context.triggerCharacter)
if (context.triggerCharacter === '"' || context.triggerCharacter === '@' || context.triggerCharacter === '/') { if (context.triggerCharacter === '"' || context.triggerCharacter === '@' || context.triggerCharacter === '/') {
const lastpart = line.substring(0, position.column - 1).split(';').pop() const lastpart = line.substring(0, position.column - 1).split(';').pop()
console.log('lastpart', lastpart)
if (lastpart.startsWith('import')) { if (lastpart.startsWith('import')) {
const imports = await this.props.plugin.call('codeParser', 'getImports') const imports = await this.props.plugin.call('codeParser', 'getImports')
if (context.triggerCharacter === '"' || context.triggerCharacter === '@') { if (context.triggerCharacter === '"' || context.triggerCharacter === '@') {
@ -46,7 +46,6 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
] ]
} else if (context.triggerCharacter === '/') { } else if (context.triggerCharacter === '/') {
const word = line.split('"')[1] const word = line.split('"')[1]
console.log(word)
suggestions = [...suggestions, suggestions = [...suggestions,
...GetImports(range, this.monaco, imports, word), ...GetImports(range, this.monaco, imports, word),
] ]

@ -13,8 +13,22 @@ export class RemixDefinitionProvider implements monaco.languages.DefinitionProvi
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
async provideDefinition(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken): Promise<monaco.languages.Definition | monaco.languages.LocationLink[]> { async provideDefinition(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken): Promise<monaco.languages.Definition | monaco.languages.LocationLink[]> {
const cursorPosition = this.props.editorAPI.getCursorPosition() const cursorPosition = this.props.editorAPI.getCursorPosition()
const jumpLocation = await this.jumpToDefinition(cursorPosition) let jumpLocation = await this.jumpToDefinition(cursorPosition)
if (!jumpLocation || !jumpLocation.fileName) {
const line = model.getLineContent(position.lineNumber)
const lastpart = line.substring(0, position.column - 1).split(';').pop()
if (lastpart.startsWith('import')) {
const importPath = line.substring(lastpart.indexOf('"') + 1)
const importPath2 = importPath.substring(0, importPath.indexOf('"'))
jumpLocation = {
startLineNumber: 1,
startColumn: 1,
endColumn: 1,
endLineNumber: 1,
fileName: importPath2
}
}
}
return [{ return [{
uri: this.monaco.Uri.parse(jumpLocation.fileName), uri: this.monaco.Uri.parse(jumpLocation.fileName),
range: { range: {

@ -1 +1,3 @@
export { SearchTab } from './lib/components/Search'; export { SearchTab } from './lib/components/Search';
export * from './lib/components/results/SearchHelper';
export * from './lib/types';

@ -109,7 +109,7 @@
"watch": "watchify apps/remix-ide/src/index.js -dv -p browserify-reload -o apps/remix-ide/build/app.js --exclude solc", "watch": "watchify apps/remix-ide/src/index.js -dv -p browserify-reload -o apps/remix-ide/build/app.js --exclude solc",
"reinstall": "rm ./node-modules/ -rf && rm yarn.lock && rm ./build/ -rf && yarn install & yarn run build", "reinstall": "rm ./node-modules/ -rf && rm yarn.lock && rm ./build/ -rf && yarn install & yarn run build",
"ganache-cli": "npx ganache-cli", "ganache-cli": "npx ganache-cli",
"build-contracts": "find ./node_modules/@openzeppelin/contracts | grep -i '.sol' > libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt && find ./node_modules/@uniswap/v3-core/contracts | grep -i '.sol' >> libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt" "build-contracts": "find ./node_modules/@hq20/contracts | grep -i '.sol' > libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt && find ./node_modules/@openzeppelin/contracts | grep -i '.sol' >> libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt && find ./node_modules/@uniswap/v3-core/contracts | grep -i '.sol' >> libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt"
}, },
"browserify": { "browserify": {
"transform": [ "transform": [

Loading…
Cancel
Save