diff options
author | Mats Larsen <me@supergrecko.com> | 2022-05-31 03:30:04 +0200 |
---|---|---|
committer | Mats Larsen <me@supergrecko.com> | 2022-05-31 03:30:04 +0200 |
commit | 2f0c1453951db63e003c6a44c7e0031de16166e4 (patch) | |
tree | 7728e071bdfdc680ca8a1235e597730201a717a3 | |
parent | ea92e0c4d53ae468e4b4779508925fccc6897c97 (diff) | |
download | compiler-explorer-gh-3180.tar.gz compiler-explorer-gh-3180.zip |
-rw-r--r-- | static/compiler-service.js | 3 | ||||
-rw-r--r-- | static/extensions/view-assembly-documentation.ts | 133 | ||||
-rw-r--r-- | static/panes/compiler.js | 147 | ||||
-rw-r--r-- | types/features/assembly-documentation.interfaces.ts | 4 |
4 files changed, 158 insertions, 129 deletions
diff --git a/static/compiler-service.js b/static/compiler-service.js index 02089123b..91de2de34 100644 --- a/static/compiler-service.js +++ b/static/compiler-service.js @@ -35,9 +35,6 @@ function CompilerService(eventHub) { this.allowStoreCodeDebug = true; this.cache = new LruCache({ max: 200 * 1024, - length: function (n) { - return JSON.stringify(n).length; - }, }); this.compilersByLang = {}; _.each( diff --git a/static/extensions/view-assembly-documentation.ts b/static/extensions/view-assembly-documentation.ts new file mode 100644 index 000000000..def21cef0 --- /dev/null +++ b/static/extensions/view-assembly-documentation.ts @@ -0,0 +1,133 @@ +import * as monaco from 'monaco-editor'; +import {ga} from '../analytics'; +import LRUCache from 'lru-cache'; +import {getAssemblyDocumentation} from '../api/api'; +import {AssemblyInstructionInfo} from '../../lib/asm-docs/base'; +import {InstructionSet} from '../../types/features/assembly-documentation.interfaces'; +import {Alert} from '../alert'; + +type OpcodeCacheEntry = + | { + found: true; + body: AssemblyInstructionInfo; + } + | { + found: false; + error: string; + }; + +const VIEW_ASSEMBLY_DOCUMENTATION_ID = 'viewasmdoc'; +const ASSEMBLY_OPCODE_CACHE = new LRUCache<string, OpcodeCacheEntry>({ + max: 64 * 1024, +}); + +/** + * Add an extension to the monaco editor which allows to view the assembly documentation for the instruction the + * cursor is currently on. + */ +export const createViewAssemblyDocumentationAction = ( + editor: monaco.editor.IStandaloneCodeEditor, + instructionSet: InstructionSet +) => { + editor.addAction({ + id: VIEW_ASSEMBLY_DOCUMENTATION_ID, + label: 'View assembly documentation', + keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.F8], + precondition: 'isAsmKeyword', + contextMenuGroupId: 'help', + contextMenuOrder: 1.5, + run: onAssemblyAction(editor, instructionSet), + }); +}; + +const onAssemblyAction = (editor: monaco.editor.IStandaloneCodeEditor, instructionSet: InstructionSet) => async () => { + ga.proxy('send', { + hitType: 'event', + eventCategory: 'OpenModalPane', + eventAction: 'AsmDocs', + }); + + const position = editor.getPosition(); + const model = editor.getModel(); + if (position === null || model === null) return; + + const word = model.getWordAtPosition(position); + if (word === null || word.word === '') return; + + const opcode = word.word.toUpperCase(); + const alertSystem = new Alert(); + + try { + const response = await getAssemblyInfo(opcode, instructionSet); + if (response.found) { + alertSystem.alert( + opcode + ' help', + response.body.html + createDisplayableHtml(response.body.url, opcode), + () => { + editor.focus(); + editor.setPosition(position); + } + ); + } else { + alertSystem.notify('This token was not found in the documentation. Sorry!', { + group: 'notokenindocs', + alertClass: 'notification-error', + dismissTime: 5000, + }); + } + } catch (error) { + alertSystem.notify('There was a network error fetching the documentation for this opcode (' + error + ').', { + group: 'notokenindocs', + alertClass: 'notification-error', + dismissTime: 5000, + }); + } +}; + +export const getAssemblyInfo = async (opcode: string, instructionSet: InstructionSet): Promise<OpcodeCacheEntry> => { + const entryName = `asm/${instructionSet}/${opcode}`; + + if (ASSEMBLY_OPCODE_CACHE.has(entryName)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- logically sound + return ASSEMBLY_OPCODE_CACHE.get(entryName)!; + } + + try { + const response = await getAssemblyDocumentation({opcode, instructionSet}); + const json = await response.json(); + if (response.status === 200) { + ASSEMBLY_OPCODE_CACHE.set(entryName, {found: true, body: json}); + } else { + // TODO(supergrecko): make prettier with XOR type + const jsonWithError = json as unknown as {error: string}; + ASSEMBLY_OPCODE_CACHE.set(entryName, {found: false, error: jsonWithError.error}); + } + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- logically sound + return ASSEMBLY_OPCODE_CACHE.get(entryName)!; + } catch (error) { + throw new Error('Fetch Assembly Documentation failed: ' + error); + } +}; + +const createDisplayableHtml = (url: string, opcode: string) => { + const title = encodeURI(`[BUG] Problem with ${opcode} opcode documentation`); + const github = `https://github.com/compiler-explorer/compiler-explorer/issues/new?title=${title}`; + return ` +<br><br> +For more information, visit +<a href='${url}' target='_blank' rel='noopener noreferrer'>the ${opcode} documentation + <sup> + <small class='fas fa-external-link-alt opens-new-window' title='Opens in a new window'> + </small> + </sup> +<a/>. +If the documentation for this opcode is wrong or broken in some way, please feel free to +<a href="${github}"> + open an issue on GitHub + <sup> + <small class='fas fa-external-link-alt opens-new-window' title='Opens in a new window'> + </small> + </sup> +</a>`; +}; diff --git a/static/panes/compiler.js b/static/panes/compiler.js index f36a5bf8d..166d3597d 100644 --- a/static/panes/compiler.js +++ b/static/panes/compiler.js @@ -31,7 +31,7 @@ var Toggles = require('../widgets/toggles').Toggles; var FontScale = require('../widgets/fontscale').FontScale; var Promise = require('es6-promise').Promise; var Components = require('../components'); -var LruCache = require('lru-cache'); +require('lru-cache'); var options = require('../options').options; var monaco = require('monaco-editor'); var Alert = require('../alert').Alert; @@ -44,16 +44,12 @@ var CompilerPicker = require('../compiler-picker').CompilerPicker; var Settings = require('../settings').Settings; var utils = require('../utils'); var LibUtils = require('../lib-utils'); -var getAssemblyDocumentation = require('../api/api').getAssemblyDocumentation; +// eslint-disable-next-line max-len +var createViewAssemblyDocumentationAction = + require('../extensions/view-assembly-documentation').createViewAssemblyDocumentationAction; +var getAssemblyInfo = require('../extensions/view-assembly-documentation').getAssemblyInfo; var PaneRenaming = require('../widgets/pane-renaming').PaneRenaming; -var OpcodeCache = new LruCache({ - max: 64 * 1024, - length: function (n) { - return JSON.stringify(n).length; - }, -}); - function patchOldFilters(filters) { if (filters === undefined) return undefined; // Filters are of the form {filter: true|false¸ ...}. In older versions, we used @@ -808,16 +804,7 @@ Compiler.prototype.initEditorActions = function () { }, this), }); - this.outputEditor.addAction({ - id: 'viewasmdoc', - label: 'View assembly documentation', - keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.F8], - keybindingContext: null, - precondition: 'isAsmKeyword', - contextMenuGroupId: 'help', - contextMenuOrder: 1.5, - run: _.bind(this.onAsmToolTip, this), - }); + createViewAssemblyDocumentationAction(this.outputEditor, this.compiler.instructionSet || 'amd64'); this.outputEditor.addAction({ id: 'toggleColourisation', @@ -981,38 +968,39 @@ Compiler.prototype.compileFromTree = function (options, bypassCache) { files: tree.multifileService.getFiles(), }; - const fetches = []; + var fetches = []; fetches.push( - this.compilerService.expand(request.source).then(contents => { + this.compilerService.expand(request.source).then(function (contents) { request.source = contents; }) ); - for (let file of request.files) { + for (var i = 0; i < request.files.length; i++) { fetches.push( - this.compilerService.expand(file.contents).then(contents => { - file.contents = contents; + this.compilerService.expand(request.files[i].contents).then(function (contents) { + request.files[i].contents = contents; }) ); } - Promise.all(fetches).then(() => { + var self = this; + Promise.all(fetches).then(function () { var treeState = tree.currentState(); var cmakeProject = tree.multifileService.isACMakeProject(); if (bypassCache) request.bypassCache = true; - if (!this.compiler) { - this.onCompileResponse(request, errorResult('<Please select a compiler>'), false); + if (!self.compiler) { + self.onCompileResponse(request, errorResult('<Please select a compiler>'), false); } else if (cmakeProject && request.source === '') { - this.onCompileResponse(request, errorResult('<Please supply a CMakeLists.txt>'), false); + self.onCompileResponse(request, errorResult('<Please supply a CMakeLists.txt>'), false); } else { if (cmakeProject) { request.options.compilerOptions.cmakeArgs = treeState.cmakeArgs; request.options.compilerOptions.customOutputFilename = treeState.customOutputFilename; - this.sendCMakeCompile(request); + self.sendCMakeCompile(request); } else { - this.sendCompile(request); + self.sendCompile(request); } } }); @@ -2385,8 +2373,9 @@ function htmlEncode(rawStr) { Compiler.prototype.checkForHints = function (result) { if (result.hints) { - result.hints.forEach(hint => { - this.alertSystem.notify(htmlEncode(hint), { + var self = this; + result.hints.forEach(function (hint) { + self.alertSystem.notify(htmlEncode(hint), { group: 'hints', collapseSimilar: false, }); @@ -2815,34 +2804,6 @@ function getNumericToolTip(value) { return result; } -function getAsmInfo(opcode, instructionSet) { - var cacheName = 'asm/' + (instructionSet ? instructionSet + '/' : '') + opcode; - var cached = OpcodeCache.get(cacheName); - if (cached) { - if (cached.found) { - return Promise.resolve(cached.data); - } - return Promise.reject(cached.data); - } - return new Promise(function (resolve, reject) { - getAssemblyDocumentation({opcode: opcode, instructionSet: instructionSet}) - .then(function (response) { - response.json().then(function (body) { - if (response.status === 200) { - OpcodeCache.set(cacheName, {found: true, data: body}); - resolve(body); - } else { - OpcodeCache.set(cacheName, {found: false, data: body.error}); - reject(body.error); - } - }); - }) - .catch(function (error) { - reject('Fetch error: ' + error); - }); - }); -} - Compiler.prototype.onDidChangeCursorSelection = function (e) { if (this.awaitingInitialResults) { this.selection = e.selection; @@ -2933,7 +2894,7 @@ Compiler.prototype.onMouseMove = function (e) { } var hoverShowAsmDoc = this.settings.hoverShowAsmDoc === true; if (hoverShowAsmDoc && this.compiler && this.compiler.supportsAsmDocs && this.isWordAsmKeyword(currentWord)) { - getAsmInfo(currentWord.word, this.compiler.instructionSet).then( + getAssemblyInfo(currentWord.word.toUpperCase(), this.compiler.instructionSet).then( _.bind(function (response) { if (!response) return; this.decorations.asmToolTip = { @@ -2969,70 +2930,6 @@ Compiler.prototype.isWordAsmKeyword = function (word) { }); }; -Compiler.prototype.onAsmToolTip = function (ed) { - ga.proxy('send', { - hitType: 'event', - eventCategory: 'OpenModalPane', - eventAction: 'AsmDocs', - }); - var pos = ed.getPosition(); - if (!pos || !ed.getModel()) return; - var word = ed.getModel().getWordAtPosition(pos); - if (!word || !word.word) return; - var opcode = word.word.toUpperCase(); - - function newGitHubIssueUrl() { - return ( - 'https://github.com/compiler-explorer/compiler-explorer/issues/new?title=' + - encodeURIComponent('[BUG] Problem with ' + opcode + ' opcode') - ); - } - - function appendInfo(url) { - return ( - '<br><br>For more information, visit <a href="' + - url + - '" target="_blank" rel="noopener noreferrer">the ' + - opcode + - ' documentation <sup><small class="fas fa-external-link-alt opens-new-window"' + - ' title="Opens in a new window"></small></sup></a>.' + - '<br>If the documentation for this opcode is wrong or broken in some way, ' + - 'please feel free to <a href="' + - newGitHubIssueUrl() + - '" target="_blank" rel="noopener noreferrer">' + - 'open an issue on GitHub <sup><small class="fas fa-external-link-alt opens-new-window" ' + - 'title="Opens in a new window"></small></sup></a>.' - ); - } - - getAsmInfo(word.word, this.compiler.instructionSet).then( - _.bind(function (asmHelp) { - if (asmHelp) { - this.alertSystem.alert(opcode + ' help', asmHelp.html + appendInfo(asmHelp.url), function () { - ed.focus(); - ed.setPosition(pos); - }); - } else { - this.alertSystem.notify('This token was not found in the documentation. Sorry!', { - group: 'notokenindocs', - alertClass: 'notification-error', - dismissTime: 5000, - }); - } - }, this), - _.bind(function (rejection) { - this.alertSystem.notify( - 'There was an error fetching the documentation for this opcode (' + rejection + ').', - { - group: 'notokenindocs', - alertClass: 'notification-error', - dismissTime: 5000, - } - ); - }, this) - ); -}; - Compiler.prototype.handleCompilationStatus = function (status) { this.compilerService.handleCompilationStatus(this.statusLabel, this.statusIcon, status); }; diff --git a/types/features/assembly-documentation.interfaces.ts b/types/features/assembly-documentation.interfaces.ts index 4d0359623..4ccde2830 100644 --- a/types/features/assembly-documentation.interfaces.ts +++ b/types/features/assembly-documentation.interfaces.ts @@ -24,9 +24,11 @@ import {AssemblyInstructionInfo} from '../../lib/asm-docs/base'; +export type InstructionSet = 'amd64' | 'arm32' | 'avr' | 'java' | 'llvm' | 'mos6502'; + export interface AssemblyDocumentationRequest { /** Specifies which instruction set to look for */ - instructionSet: 'amd64' | 'arm32' | 'java'; + instructionSet: InstructionSet; /** Instruction set opcode to look for */ opcode: string; } |