diff options
| author | alexwl <alexey.a.kiryushin@gmail.com> | 2018-10-02 13:17:04 +0300 | 
|---|---|---|
| committer | alexwl <alexey.a.kiryushin@gmail.com> | 2018-10-02 13:17:04 +0300 | 
| commit | cf2c56c7061b7ed40fdd3b40a352ddb9c9b7371f (patch) | |
| tree | b1de9ada0f1b1cb064e3a9e0d4042d1f519085bd /javascript/app/utils | |
Initial commit
Diffstat (limited to 'javascript/app/utils')
| -rw-r--r-- | javascript/app/utils/api-urls.js | 29 | ||||
| -rw-r--r-- | javascript/app/utils/color-themes.js | 188 | ||||
| -rw-r--r-- | javascript/app/utils/go-to-definition.js | 95 | ||||
| -rw-r--r-- | javascript/app/utils/line-selection.js | 111 | 
4 files changed, 423 insertions, 0 deletions
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)); //<td "id"="LC10">...</td> +    if(number >= startLine && number <= endLine) { +      lineElement.classList.add('highlighted-line'); +    } else { +      lineElement.classList.remove('highlighted-line'); +    } +  }); +} + +export { +  initializeLineSelection,highlightLines,highlightSelectedLines +}  | 
