From cf2c56c7061b7ed40fdd3b40a352ddb9c9b7371f Mon Sep 17 00:00:00 2001 From: alexwl Date: Tue, 2 Oct 2018 13:17:04 +0300 Subject: Initial commit --- javascript/app/utils/api-urls.js | 29 +++++ javascript/app/utils/color-themes.js | 188 +++++++++++++++++++++++++++++++ javascript/app/utils/go-to-definition.js | 95 ++++++++++++++++ javascript/app/utils/line-selection.js | 111 ++++++++++++++++++ 4 files changed, 423 insertions(+) create mode 100644 javascript/app/utils/api-urls.js create mode 100644 javascript/app/utils/color-themes.js create mode 100644 javascript/app/utils/go-to-definition.js create mode 100644 javascript/app/utils/line-selection.js (limited to 'javascript/app/utils') diff --git a/javascript/app/utils/api-urls.js b/javascript/app/utils/api-urls.js new file mode 100644 index 0000000..b2748b6 --- /dev/null +++ b/javascript/app/utils/api-urls.js @@ -0,0 +1,29 @@ +import config from '../config/environment'; + +export const urls = { + packageInfoUrl : function(packageId) { + return config.APP.staticUrlPrefix+"/"+packageId+"/"+config.APP.haskellCodeExplorerDirectory+"/packageInfo.json"; + }, + fileUrl : function(packageId,filePath) { + return config.APP.staticUrlPrefix+"/"+packageId+"/"+filePath; + }, + haskellModuleUrl : function (packageId,filePath) { + return config.APP.staticUrlPrefix+"/"+packageId+"/"+config.APP.haskellCodeExplorerDirectory+"/"+encodeURIComponent(encodeURIComponent(filePath))+ ".json"; + }, + packagesUrl : config.APP.apiUrlPrefix + "/packages", + identifierDefinitionSiteUrl : function(packageId,moduleName,componentId,entity,name) { + return config.APP.apiUrlPrefix + "/definitionSite/" + packageId+"/"+componentId+"/"+moduleName+"/"+entity+"/"+encodeURIComponent(name).replace(/\./g, '%2E'); + }, + modulePathUrl : function (packageId,moduleName,componentId) { + return config.APP.apiUrlPrefix + "/modulePath/"+packageId+"/"+componentId+"/"+moduleName; + }, + expressionsUrl : function (packageId,modulePath,lineStart,columnStart,lineEnd,columnEnd) { + return config.APP.apiUrlPrefix + "/expressions/"+packageId+"/"+encodeURIComponent(modulePath) +"/"+lineStart+"/"+columnStart+"/"+lineEnd+"/"+columnEnd; + }, + referencesUrl : function (packageId,externalId) { + return config.APP.apiUrlPrefix + "/references/"+packageId+"/"+encodeURIComponent(externalId); + }, + identifierSearchUrl : function (packageId,query) { + return config.APP.apiUrlPrefix + "/identifiers/"+packageId+"/"+encodeURIComponent(query).replace(/\./g, '%2E'); + } +} diff --git a/javascript/app/utils/color-themes.js b/javascript/app/utils/color-themes.js new file mode 100644 index 0000000..0a563ff --- /dev/null +++ b/javascript/app/utils/color-themes.js @@ -0,0 +1,188 @@ +function colorThemeToCss(colorTheme) { + const css = ` + body { + color: ${colorTheme.defaultColor} !important; + background-color: ${colorTheme.backgroundColor} !important; + } + input { + color: ${colorTheme.defaultColor} !important; + background-color: ${colorTheme.backgroundColor} !important; + border-color: ${colorTheme.borderColor} !important; + } + .package-content { + border-top: 1px solid ${colorTheme.borderColor} !important; + } + .header a { + color : ${colorTheme.menuLinkColor} !important; + } + a { + color: ${colorTheme.typeColor} !important; + } + span.link { + color: ${colorTheme.typeColor} !important; + } + .header { + background-color: ${colorTheme.menuColor} !important; + border-bottom: 1px solid ${colorTheme.borderColor} !important; + } + .declarations-content { + background-color: ${colorTheme.navigationPanelColor} !important; + border: 1px solid ${colorTheme.borderColor} !important; + } + .declarations-header { + background-color: ${colorTheme.navigationPanelColor} !important; + border: 1px solid ${colorTheme.borderColor} !important; + } + li.declaration { + border-bottom: 1px solid ${colorTheme.borderColor} !important; + } + .left-panel { + background-color: ${colorTheme.navigationPanelColor} !important; + border-right: 1px solid ${colorTheme.borderColor} !important; + } + .show-left-panel-button { + background-color: ${colorTheme.navigationPanelColor} !important; + border-right:1px solid ${colorTheme.borderColor} !important; + border-bottom:1px solid ${colorTheme.borderColor} !important; + } + .right-panel { + background-color: ${colorTheme.backgroundColor} !important; + } + a.jstree-anchor { + color: ${colorTheme.defaultColor} !important; + } + .declaration > a { + color: ${colorTheme.defaultColor} !important; + } + .highlighted-line { + background : ${colorTheme.highlightedLineColor} !important; + } + table.source-code { + background-color: ${colorTheme.backgroundColor} !important; + color: ${colorTheme.defaultColor} !important; + } + .jstree-clicked { + background-color: ${colorTheme.backgroundColor} !important; + } + .jstree-hovered { + background-color: ${colorTheme.backgroundColor} !important; + } + ul.autocomplete-items { + background-color: ${colorTheme.backgroundColor} !important; + border-top: 1px solid ${colorTheme.borderColor} !important; + border-left: 1px solid ${colorTheme.borderColor} !important; + border-right: 1px solid ${colorTheme.borderColor} !important; + } + ul.autocomplete-items > li { + border-bottom: 1px solid ${colorTheme.borderColor} !important; + } + ul.autocomplete-items > li:hover { + background-color: ${colorTheme.highlightedLineColor} !important; + } + ul.autocomplete-items > li.highlighted { + background-color: ${colorTheme.highlightedLineColor} !important; + } + .source-code-snippet { + color: ${colorTheme.defaultColor} !important; + border-bottom: 1px solid ${colorTheme.borderColor} !important; + } + .source-code-snippet:hover { + background-color: ${colorTheme.highlightedLineColor} !important; + } + .bottom-panel { + background-color: ${colorTheme.backgroundColor} !important; + border-top: 1px solid ${colorTheme.borderColor} !important; + } + .bottom-panel-header { + border-bottom: 1px solid ${colorTheme.borderColor} !important; + } + .paginated-list-header { + border-bottom: 1px solid ${colorTheme.borderColor} !important; + } + li.search-result { + border-bottom: 1px solid ${colorTheme.borderColor} !important; + } + .search-results-header { + border-bottom: 1px solid ${colorTheme.borderColor} !important; + } + .info-window-content { + border-top: 1px solid ${colorTheme.borderColor} !important; + } + .info-window { + border: 1px solid ${colorTheme.borderColor} !important; + background-color:${colorTheme.infoWindowColor} !important; + color: ${colorTheme.defaultColor} !important; + } + .type-info { + border-top: 1px solid ${colorTheme.borderColor} !important; + }`; + return css; +} + +const darkTheme = { + id: "darkTheme", + name: "Dark theme", + description: "Dark theme (Monokai based)", + defaultColor: "#F8F8F2", + backgroundColor: "#272822", + typeColor: "#66D9EF", + literalColor: "#E6DB74", + topLevelIdFromCurrentModule : "#A6E22E", + localIdentifierColor: ["#F0A3FF","#0075DC","#993F00", + "#2BCE48","#FFCC99","#808080","#94FFB5","#8F7C00", + "#C20088","#FFA405","#FFA8BB","#426600","#FF0010", + "#5EF1F2","#00998F","#E0FF66","#FFFF80", + "#FFFF00","#FF5005"], + menuColor: "#3c3b37", + menuLinkColor : "#F8F8F2", + infoWindowColor: "#3c3b37", + navigationPanelColor: "#3c3b37", + linkColor : "#0366d6", + borderColor: "#535557", + highlightedLineColor: "#4a4a4a" +}; + +const lightTheme = { + id: "lightTheme", + name: "Light theme", + description: "Light theme (Github based)", + defaultColor: "#24292e", + backgroundColor: "#ffffff", + typeColor: "#005cc5", + literalColor: "#032f62", + topLevelIdFromCurrentModule : "#6f42c1", + localIdentifierColor: ["#005C31", + "#2BCE48","#808080","#8F7C00", + "#C20088","#FFA405","#ffa8bb","#426600","#FF0010", + "#09d7d8","#00998F","#990000","#FF5005"], + menuColor: "#f2f4f8", + menuLinkColor : "#24292e", + infoWindowColor: "#f2f4f8", + navigationPanelColor: "#f2f4f8", + linkColor : "#0366d6", + borderColor: "#e1e4e8", + highlightedLineColor: "#eaeaea" +}; + +function updateColorThemeCss (colorTheme) { + const newStyle = document.createElement('style'); + newStyle.type = 'text/css'; + newStyle.innerHTML = colorThemeToCss(colorTheme); + newStyle.id = 'color-theme'; + const oldStyle = document.querySelector("style#color-theme"); + if(oldStyle) { + oldStyle.parentElement.removeChild(oldStyle); + } + document.getElementsByTagName('head')[0].appendChild(newStyle); +} + +const themes = { + darkTheme: darkTheme, + lightTheme: lightTheme +}; + +export { + updateColorThemeCss, + colorThemeToCss, + themes +} diff --git a/javascript/app/utils/go-to-definition.js b/javascript/app/utils/go-to-definition.js new file mode 100644 index 0000000..e05f6bf --- /dev/null +++ b/javascript/app/utils/go-to-definition.js @@ -0,0 +1,95 @@ +function exactLocationToUrl(exactLocation) { + const modulePath = exactLocation.modulePath; + const packageId = exactLocation.packageId.name + "-" + exactLocation.packageId.version; + let hash = ""; + if(exactLocation.startLine != 1) { + hash = "#L" + exactLocation.startLine; + } + return "/package/"+packageId+"/show/"+modulePath+hash; +} + +function hackageUrl(packageId,locationInfo) { + const dasherizedModuleName = locationInfo.moduleName.replace(/\./g,'-'); + let key; + if(locationInfo.entity === "Val") { + key = "v"; + } else { + key = "t"; + } + let hash = ""; + if(locationInfo.entity === "Val" || locationInfo.entity === "Typ") { + hash = "#"+key+":"+locationInfo.haddockAnchorId; + } + return "https://hackage.haskell.org/package/"+packageId+"/docs/"+dasherizedModuleName+".html"+hash; +} + +function openUrl(buttonId,url) { + if(buttonId === 2) {//middle mouse button + window.open(url, '_blank'); + } else if(buttonId == 1) {//left mouse button + window.location = url; + } + return false; +} + +function saveCurrentLocation(currentLineNumber) { + if(currentLineNumber) { + const url = window.location.origin + window.location.pathname + "#L" + currentLineNumber; + if(location.href != url) { + window.location.hash = "#L" + currentLineNumber; + } + } +} + +function goToDefinition(store,locationInfo,buttonId,currentLineNumber) { + if(locationInfo.tag === "ExactLocation") { + const url = exactLocationToUrl(locationInfo); + if(locationInfo.startLine !== currentLineNumber) { + saveCurrentLocation(currentLineNumber); + } + openUrl(buttonId,url); + } else if(locationInfo.tag === "ApproximateLocation") { + const packageId = locationInfo.packageId.name+"-"+locationInfo.packageId.version; + if(locationInfo.entity === "Mod") { + store.loadDefinitionSite(packageId, + locationInfo.moduleName, + locationInfo.componentId, + locationInfo.entity, + locationInfo.moduleName) + .then((defSite) => { + const packageId = defSite.location.packageId.name + "-" + defSite.location.packageId.version; + openUrl(buttonId,"/package/" + packageId + "/show/" + defSite.location.modulePath); + }).catch(() => { + openUrl(buttonId,hackageUrl(packageId,locationInfo)); + }); + } else { + store.loadDefinitionSite(packageId, + locationInfo.moduleName, + locationInfo.componentId, + locationInfo.entity, + locationInfo.name) + .then((definitionSite) => { + if(definitionSite.location.tag === "ExactLocation") { + const url = exactLocationToUrl(definitionSite.location); + if(locationInfo.startLine !== currentLineNumber) { + saveCurrentLocation(currentLineNumber); + } + openUrl(buttonId,url); + } else { + saveCurrentLocation(currentLineNumber); + openUrl(buttonId,hackageUrl(packageId,locationInfo)); + } + }).catch((e) => { + console.log(e); + saveCurrentLocation(currentLineNumber); + openUrl(buttonId,hackageUrl(packageId,locationInfo)); + }); + } + } else { + alert('No location info'); + } +} + +export { + goToDefinition,openUrl +} diff --git a/javascript/app/utils/line-selection.js b/javascript/app/utils/line-selection.js new file mode 100644 index 0000000..d6d0976 --- /dev/null +++ b/javascript/app/utils/line-selection.js @@ -0,0 +1,111 @@ +function initializeLineSelection(sourceCodeContainerElement,component) { + const lineNumbers = Array.prototype.slice.call(sourceCodeContainerElement.querySelectorAll("td.line-number")); + if(lineNumbers.length > 0) { + const onhashchange = function () { + highlightSelectedLines(sourceCodeContainerElement); + } + window.addEventListener("hashchange",onhashchange); + component._onhashchange = onhashchange; + + let shiftPressed; + const onkeydown = function (event) { + if(event.keyCode === 16) { shiftPressed = true; } + }; + const onkeyup = function (event) { + if(event.keyCode === 16) { shiftPressed = false; } + }; + + document.addEventListener('keydown',onkeydown); + document.addEventListener('keyup',onkeyup); + component._onkeydown = onkeydown; + component._onkeyup = onkeyup; + + let selectedLine1,selectedLine2; + lineNumbers.forEach((lineNumberElement) => { + lineNumberElement.onclick = function() { + const number = parseInt(this.textContent); + if(shiftPressed && selectedLine1) { + if(selectedLine1 != number) { + selectedLine2 = number; + if(selectedLine1 < selectedLine2) { + highlightLines(sourceCodeContainerElement,selectedLine1,selectedLine2); + window.location.hash = "L"+selectedLine1+"-L"+selectedLine2; + } else { + highlightLines(sourceCodeContainerElement,selectedLine2,selectedLine1); + window.location.hash = "L"+selectedLine2+"-L"+selectedLine1; + } + } + } else { + selectedLine1 = number; + selectedLine2 = null; + highlightLines(sourceCodeContainerElement,selectedLine1,selectedLine1); + window.location.hash = "L"+number; + } + } + }); + const lines = highlightSelectedLines(sourceCodeContainerElement); + if(lines.length) { + selectedLine1 = lines[0]; + selectedLine2 = lines[1]; + } + } +} + +function highlightSelectedLines (sourceCodeContainerElement) { + const lineInfo = window.location.hash.slice(1); + if(lineInfo) { + if(lineInfo.includes('-')) { + const lines = lineInfo.split("-"); + const lineNumber1 = parseInt(lines[0].substring(1)); + const lineNumber2 = parseInt(lines[1].substring(1)); + if(lineNumber1 && lineNumber2 && lineNumber1 <= lineNumber2) { + highlightLines(sourceCodeContainerElement,lineNumber1,lineNumber2); + const line = sourceCodeContainerElement.querySelector("td#LC"+lineNumber1); + if(line) { + scrollLineIntoView(line,sourceCodeContainerElement); + } + return [lineNumber1,lineNumber2]; + } + } else { + const lineNumber = parseInt(lineInfo.substring(1)); + if(lineNumber) { + highlightLines(sourceCodeContainerElement,lineNumber,lineNumber); + const line = sourceCodeContainerElement.querySelector("td#LC"+lineNumber); + if(line) { + scrollLineIntoView(line,sourceCodeContainerElement); + } + return [lineNumber]; + } + } + } else { + highlightLines(sourceCodeContainerElement,0,0); + return []; + } +} + +function scrollLineIntoView(lineElement,sourceCodeContainerElement) { + lineElement.parentNode.scrollIntoView(); + const container = sourceCodeContainerElement.parentNode.parentNode; + const windowHeight = container.offsetHeight; + const fullHeight = sourceCodeContainerElement.offsetHeight; + + if(fullHeight - container.scrollTop > windowHeight) { + container.scrollTop = container.scrollTop - (windowHeight/2 - 20); + } +} + +function highlightLines(parentElement,startLine,endLine) { + const lineElements = Array.prototype.slice.call(parentElement.querySelectorAll("td.line-content")); + lineElements.forEach((lineElement) => { + const number = parseInt(lineElement.id.substring(2)); //... + if(number >= startLine && number <= endLine) { + lineElement.classList.add('highlighted-line'); + } else { + lineElement.classList.remove('highlighted-line'); + } + }); +} + +export { + initializeLineSelection,highlightLines,highlightSelectedLines +} -- cgit v1.2.3