mirror of
https://github.com/open-goal/opengoal-vscode.git
synced 2024-10-19 20:47:37 -04:00
goal: Basic parinfer integration (#173)
This commit is contained in:
parent
c8a0286ff3
commit
774f65b015
11
.github/workflows/build.yaml
vendored
11
.github/workflows/build.yaml
vendored
|
@ -22,9 +22,10 @@ jobs:
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
cache: npm
|
cache: yarn
|
||||||
|
|
||||||
- name: Install NPM Dependencies and Build Extension
|
- name: Install Dependencies
|
||||||
run: |
|
run: yarn install --frozen-lockfile
|
||||||
npm ci
|
|
||||||
npm run compile
|
- name: Build Extension
|
||||||
|
run: yarn compile
|
||||||
|
|
23
.github/workflows/lint.yaml
vendored
23
.github/workflows/lint.yaml
vendored
|
@ -20,12 +20,14 @@ jobs:
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
cache: npm
|
cache: yarn
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Check Linting
|
||||||
|
run: yarn lint
|
||||||
|
|
||||||
- name: Install Dependencies and Check Formatting
|
|
||||||
run: |
|
|
||||||
npm ci
|
|
||||||
npm run lint
|
|
||||||
formatting:
|
formatting:
|
||||||
name: Formatting
|
name: Formatting
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -37,9 +39,10 @@ jobs:
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
cache: npm
|
cache: yarn
|
||||||
|
|
||||||
- name: Install Dependencies and Check Formatting
|
- name: Install Dependencies
|
||||||
run: |
|
run: yarn install --frozen-lockfile
|
||||||
npm ci
|
|
||||||
npm run format:check
|
- name: Check Linting
|
||||||
|
run: yarn format:check
|
||||||
|
|
10
.github/workflows/release.yaml
vendored
10
.github/workflows/release.yaml
vendored
|
@ -25,7 +25,10 @@ jobs:
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
cache: npm
|
cache: yarn
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Bump Version
|
- name: Bump Version
|
||||||
env:
|
env:
|
||||||
|
@ -33,9 +36,8 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
git config --global user.name "OpenGOALBot"
|
git config --global user.name "OpenGOALBot"
|
||||||
git config --global user.email "OpenGOALBot@users.noreply.github.com"
|
git config --global user.email "OpenGOALBot@users.noreply.github.com"
|
||||||
npm ci
|
yarn vsce package
|
||||||
npx vsce package
|
yarn vsce publish ${{ github.event.inputs.bump }}
|
||||||
npx vsce publish ${{ github.event.inputs.bump }}
|
|
||||||
git push
|
git push
|
||||||
git push origin $(git tag --points-at HEAD)
|
git push origin $(git tag --points-at HEAD)
|
||||||
|
|
||||||
|
|
463
docs/parinfer-tester/assets/codemirror.css
Normal file
463
docs/parinfer-tester/assets/codemirror.css
Normal file
|
@ -0,0 +1,463 @@
|
||||||
|
/* BASICS */
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
/* Set height, width, borders, and global font properties here */
|
||||||
|
font-family: monospace;
|
||||||
|
height: 300px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PADDING */
|
||||||
|
|
||||||
|
.CodeMirror-lines {
|
||||||
|
padding: 4px 0; /* Vertical padding around content */
|
||||||
|
}
|
||||||
|
.CodeMirror pre {
|
||||||
|
padding: 0 4px; /* Horizontal padding of content */
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-scrollbar-filler,
|
||||||
|
.CodeMirror-gutter-filler {
|
||||||
|
background-color: white; /* The little square between H and V scrollbars */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GUTTER */
|
||||||
|
|
||||||
|
.CodeMirror-gutters {
|
||||||
|
border-right: 1px solid #ddd;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.CodeMirror-linenumbers {
|
||||||
|
}
|
||||||
|
.CodeMirror-linenumber {
|
||||||
|
padding: 0 3px 0 5px;
|
||||||
|
min-width: 20px;
|
||||||
|
text-align: right;
|
||||||
|
color: #999;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-guttermarker {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.CodeMirror-guttermarker-subtle {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CURSOR */
|
||||||
|
|
||||||
|
.CodeMirror-cursor {
|
||||||
|
border-left: 1px solid black;
|
||||||
|
border-right: none;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
/* Shown when moving in bi-directional text */
|
||||||
|
.CodeMirror div.CodeMirror-secondarycursor {
|
||||||
|
border-left: 1px solid silver;
|
||||||
|
}
|
||||||
|
.cm-fat-cursor .CodeMirror-cursor {
|
||||||
|
width: auto;
|
||||||
|
border: 0;
|
||||||
|
background: #7e7;
|
||||||
|
}
|
||||||
|
.cm-fat-cursor div.CodeMirror-cursors {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-animate-fat-cursor {
|
||||||
|
width: auto;
|
||||||
|
border: 0;
|
||||||
|
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||||
|
-moz-animation: blink 1.06s steps(1) infinite;
|
||||||
|
animation: blink 1.06s steps(1) infinite;
|
||||||
|
background-color: #7e7;
|
||||||
|
}
|
||||||
|
@-moz-keyframes blink {
|
||||||
|
0% {
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-webkit-keyframes blink {
|
||||||
|
0% {
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes blink {
|
||||||
|
0% {
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can style cursor different in overwrite (non-insert) mode */
|
||||||
|
.CodeMirror-overwrite .CodeMirror-cursor {
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-tab {
|
||||||
|
display: inline-block;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-ruler {
|
||||||
|
border-left: 1px solid #ccc;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEFAULT THEME */
|
||||||
|
|
||||||
|
.cm-s-default .cm-header {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-quote {
|
||||||
|
color: #090;
|
||||||
|
}
|
||||||
|
.cm-negative {
|
||||||
|
color: #d44;
|
||||||
|
}
|
||||||
|
.cm-positive {
|
||||||
|
color: #292;
|
||||||
|
}
|
||||||
|
.cm-header,
|
||||||
|
.cm-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.cm-em {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.cm-link {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.cm-strikethrough {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-default .cm-keyword {
|
||||||
|
color: #708;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-atom {
|
||||||
|
color: #219;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-number {
|
||||||
|
color: #164;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-def {
|
||||||
|
color: #00f;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-variable,
|
||||||
|
.cm-s-default .cm-punctuation,
|
||||||
|
.cm-s-default .cm-property,
|
||||||
|
.cm-s-default .cm-operator {
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-variable-2 {
|
||||||
|
color: #05a;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-variable-3 {
|
||||||
|
color: #085;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-comment {
|
||||||
|
color: #a50;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-string {
|
||||||
|
color: #a11;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-string-2 {
|
||||||
|
color: #f50;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-meta {
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-qualifier {
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-builtin {
|
||||||
|
color: #30a;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-bracket {
|
||||||
|
color: #997;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-tag {
|
||||||
|
color: #170;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-attribute {
|
||||||
|
color: #00c;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-hr {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-link {
|
||||||
|
color: #00c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-s-default .cm-error {
|
||||||
|
color: #f00;
|
||||||
|
}
|
||||||
|
.cm-invalidchar {
|
||||||
|
color: #f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-composing {
|
||||||
|
border-bottom: 2px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default styles for common addons */
|
||||||
|
|
||||||
|
/* Parinfer edit: don't change the colors, we just want to style the background
|
||||||
|
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
|
||||||
|
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
||||||
|
*/
|
||||||
|
.CodeMirror-matchingtag {
|
||||||
|
background: rgba(255, 150, 0, 0.3);
|
||||||
|
}
|
||||||
|
.CodeMirror-activeline-background {
|
||||||
|
background: #e8f2ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STOP */
|
||||||
|
|
||||||
|
/* The rest of this file contains styles related to the mechanics of
|
||||||
|
the editor. You probably shouldn't touch them. */
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-scroll {
|
||||||
|
overflow: scroll !important; /* Things will break if this is overridden */
|
||||||
|
/* 30px is the magic margin used to hide the element's real scrollbars */
|
||||||
|
/* See overflow: hidden in .CodeMirror */
|
||||||
|
margin-bottom: -30px;
|
||||||
|
margin-right: -30px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
height: 100%;
|
||||||
|
outline: none; /* Prevent dragging from highlighting the element */
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.CodeMirror-sizer {
|
||||||
|
position: relative;
|
||||||
|
border-right: 30px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
||||||
|
before actuall scrolling happens, thus preventing shaking and
|
||||||
|
flickering artifacts. */
|
||||||
|
.CodeMirror-vscrollbar,
|
||||||
|
.CodeMirror-hscrollbar,
|
||||||
|
.CodeMirror-scrollbar-filler,
|
||||||
|
.CodeMirror-gutter-filler {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 6;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.CodeMirror-vscrollbar {
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
.CodeMirror-hscrollbar {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
overflow-y: hidden;
|
||||||
|
overflow-x: scroll;
|
||||||
|
}
|
||||||
|
.CodeMirror-scrollbar-filler {
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-filler {
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-gutters {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter {
|
||||||
|
white-space: normal;
|
||||||
|
height: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: -30px;
|
||||||
|
/* Hack to make IE7 behave */
|
||||||
|
*zoom: 1;
|
||||||
|
*display: inline;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 4;
|
||||||
|
background: none !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-elt {
|
||||||
|
position: absolute;
|
||||||
|
cursor: default;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-wrapper {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-lines {
|
||||||
|
cursor: text;
|
||||||
|
min-height: 1px; /* prevents collapsing before first draw */
|
||||||
|
}
|
||||||
|
.CodeMirror pre {
|
||||||
|
/* Reset some styles that the rest of the page might have set */
|
||||||
|
-moz-border-radius: 0;
|
||||||
|
-webkit-border-radius: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
border-width: 0;
|
||||||
|
background: transparent;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre;
|
||||||
|
word-wrap: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
color: inherit;
|
||||||
|
z-index: 2;
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
.CodeMirror-wrap pre {
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-linebackground {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-linewidget {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-widget {
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-code {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force content-box sizing for the elements where we expect it */
|
||||||
|
.CodeMirror-scroll,
|
||||||
|
.CodeMirror-sizer,
|
||||||
|
.CodeMirror-gutter,
|
||||||
|
.CodeMirror-gutters,
|
||||||
|
.CodeMirror-linenumber {
|
||||||
|
-moz-box-sizing: content-box;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-measure {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-cursor {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.CodeMirror-measure pre {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.CodeMirror-cursors {
|
||||||
|
visibility: hidden;
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
div.CodeMirror-dragcursors {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-focused div.CodeMirror-cursors {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-selected {
|
||||||
|
background: #d9d9d9;
|
||||||
|
}
|
||||||
|
.CodeMirror-focused .CodeMirror-selected {
|
||||||
|
background: #d7d4f0;
|
||||||
|
}
|
||||||
|
.CodeMirror-crosshair {
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
.CodeMirror-line::selection,
|
||||||
|
.CodeMirror-line > span::selection,
|
||||||
|
.CodeMirror-line > span > span::selection {
|
||||||
|
background: #d7d4f0;
|
||||||
|
}
|
||||||
|
.CodeMirror-line::-moz-selection,
|
||||||
|
.CodeMirror-line > span::-moz-selection,
|
||||||
|
.CodeMirror-line > span > span::-moz-selection {
|
||||||
|
background: #d7d4f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-searching {
|
||||||
|
background: #ffa;
|
||||||
|
background: rgba(255, 255, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
|
||||||
|
.CodeMirror span {
|
||||||
|
*vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used to force a border model for a node */
|
||||||
|
.cm-force-border {
|
||||||
|
padding-right: 0.1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
/* Hide the cursor when printing */
|
||||||
|
.CodeMirror div.CodeMirror-cursors {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See issue #2901 */
|
||||||
|
.cm-tab-wrap-hack:after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Help users use markselection to safely style text background */
|
||||||
|
span.CodeMirror-selectedtext {
|
||||||
|
background: none;
|
||||||
|
}
|
11556
docs/parinfer-tester/assets/codemirror.js
Normal file
11556
docs/parinfer-tester/assets/codemirror.js
Normal file
File diff suppressed because it is too large
Load diff
733
docs/parinfer-tester/assets/parinfer-codemirror.js
Normal file
733
docs/parinfer-tester/assets/parinfer-codemirror.js
Normal file
|
@ -0,0 +1,733 @@
|
||||||
|
//
|
||||||
|
// Parinfer for CodeMirror 1.4.1
|
||||||
|
//
|
||||||
|
// Copyright 2017 © Shaun Lebron
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// JS Module Boilerplate
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(function (root, factory) {
|
||||||
|
if (typeof define === "function" && define.amd) {
|
||||||
|
define([], factory);
|
||||||
|
} else if (typeof module === "object" && module.exports) {
|
||||||
|
module.exports = factory();
|
||||||
|
} else {
|
||||||
|
root.parinferCodeMirror = factory();
|
||||||
|
}
|
||||||
|
})(this, function () {
|
||||||
|
// start module anonymous scope
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// We attach our Parinfer state to this property on the CodeMirror instance.
|
||||||
|
var STATE_PROP = "__parinfer__";
|
||||||
|
|
||||||
|
var PAREN_MODE = "paren";
|
||||||
|
var INDENT_MODE = "indent";
|
||||||
|
var SMART_MODE = "smart";
|
||||||
|
|
||||||
|
var MODES = [PAREN_MODE, INDENT_MODE, SMART_MODE];
|
||||||
|
|
||||||
|
var CLASSNAME_ERROR = "parinfer-error";
|
||||||
|
var CLASSNAME_PARENTRAIL = "parinfer-paren-trail";
|
||||||
|
var CLASSNAME_LOCUS_PAREN = "parinfer-locus-paren";
|
||||||
|
|
||||||
|
var CLASSNAME_LOCUS_LAYER = "parinfer-locus";
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// State
|
||||||
|
// (`state` represents the parinfer state attached to a single CodeMirror editor)
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function initialState(cm, mode, options) {
|
||||||
|
return {
|
||||||
|
cm: cm,
|
||||||
|
mode: mode,
|
||||||
|
options: options,
|
||||||
|
enabled: false,
|
||||||
|
cursorTimeout: null,
|
||||||
|
monitorCursor: true,
|
||||||
|
prevCursorX: null,
|
||||||
|
prevCursorLine: null,
|
||||||
|
callbackCursor: null,
|
||||||
|
callbackChanges: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Errors
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function error(msg) {
|
||||||
|
return "parinferCodeMirror: " + msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureMode(mode) {
|
||||||
|
if (MODES.indexOf(mode) === -1) {
|
||||||
|
throw error(
|
||||||
|
'Mode "' +
|
||||||
|
mode +
|
||||||
|
'" is invalid. ' +
|
||||||
|
"Must be one of: " +
|
||||||
|
MODES.join(",")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureState(cm) {
|
||||||
|
var state = cm[STATE_PROP];
|
||||||
|
if (!state) {
|
||||||
|
throw error(
|
||||||
|
"You must call parinferCodeMirror.init(cm) on a CodeMirror instance " +
|
||||||
|
"before you can use the rest of the API."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Data conversion
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function convertChanges(changes) {
|
||||||
|
return changes.map(function (change) {
|
||||||
|
return {
|
||||||
|
x: change.from.ch,
|
||||||
|
lineNo: change.from.line,
|
||||||
|
oldText: change.removed.join("\n"),
|
||||||
|
newText: change.text.join("\n"),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Markers
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function clearMarks(cm, className) {
|
||||||
|
var i;
|
||||||
|
var marks = cm.getAllMarks();
|
||||||
|
for (i = 0; i < marks.length; i++) {
|
||||||
|
if (marks[i].className === className) {
|
||||||
|
marks[i].clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAllMarks(cm) {
|
||||||
|
clearMarks(cm, CLASSNAME_ERROR);
|
||||||
|
clearMarks(cm, CLASSNAME_PARENTRAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMark(cm, lineNo, x0, x1, className) {
|
||||||
|
var from = { line: lineNo, ch: x0 };
|
||||||
|
var to = { line: lineNo, ch: x1 };
|
||||||
|
cm.markText(from, to, { className: className });
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateErrorMarks(cm, error) {
|
||||||
|
clearMarks(cm, CLASSNAME_ERROR);
|
||||||
|
if (error) {
|
||||||
|
addMark(cm, error.lineNo, error.x, error.x + 1, CLASSNAME_ERROR);
|
||||||
|
if (error.extra) {
|
||||||
|
addMark(
|
||||||
|
cm,
|
||||||
|
error.extra.lineNo,
|
||||||
|
error.extra.x,
|
||||||
|
error.extra.x + 1,
|
||||||
|
CLASSNAME_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateParenTrailMarks(cm, parenTrails) {
|
||||||
|
clearMarks(cm, CLASSNAME_PARENTRAIL);
|
||||||
|
if (parenTrails) {
|
||||||
|
var i, trail;
|
||||||
|
for (i = 0; i < parenTrails.length; i++) {
|
||||||
|
trail = parenTrails[i];
|
||||||
|
addMark(
|
||||||
|
cm,
|
||||||
|
trail.lineNo,
|
||||||
|
trail.startX,
|
||||||
|
trail.endX,
|
||||||
|
CLASSNAME_PARENTRAIL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Tab Stops
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function getSelectionStartLine(cm) {
|
||||||
|
var selection = cm.listSelections()[0];
|
||||||
|
// head and anchor are reversed sometimes
|
||||||
|
return Math.min(selection.head.line, selection.anchor.line);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expandTabStops(tabStops) {
|
||||||
|
if (!tabStops) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var xs = [];
|
||||||
|
var i,
|
||||||
|
stop,
|
||||||
|
prevX = -1;
|
||||||
|
for (i = 0; i < tabStops.length; i++) {
|
||||||
|
stop = tabStops[i];
|
||||||
|
if (prevX >= stop.x) {
|
||||||
|
xs.pop();
|
||||||
|
}
|
||||||
|
xs.push(stop.x);
|
||||||
|
xs.push(stop.x + (stop.ch === "(" ? 2 : 1));
|
||||||
|
if (stop.argX != null) {
|
||||||
|
xs.push(stop.argX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return xs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextStop(stops, x, dx) {
|
||||||
|
if (!stops) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var i, stop, right, left;
|
||||||
|
for (i = 0; i < stops.length; i++) {
|
||||||
|
stop = stops[i];
|
||||||
|
if (x < stop) {
|
||||||
|
right = stop;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (x > stop) {
|
||||||
|
left = stop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dx === -1) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
if (dx === 1) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIndent(cm, lineNo) {
|
||||||
|
var line = cm.getLine(lineNo);
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < line.length; i++) {
|
||||||
|
if (line[i] !== " ") {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function indentSelection(cm, dx, stops) {
|
||||||
|
// Indent whole Selection
|
||||||
|
var lineNo = getSelectionStartLine(cm);
|
||||||
|
var x = getIndent(cm, lineNo);
|
||||||
|
var nextX = nextStop(stops, x, dx);
|
||||||
|
if (nextX == null) {
|
||||||
|
nextX = Math.max(0, x + dx * 2);
|
||||||
|
}
|
||||||
|
cm.indentSelection(nextX - x);
|
||||||
|
}
|
||||||
|
|
||||||
|
function indentLine(cm, lineNo, delta) {
|
||||||
|
var text = cm.getDoc().getLine(lineNo);
|
||||||
|
|
||||||
|
// cm.indentLine does not indent empty lines
|
||||||
|
if (text.trim() !== "") {
|
||||||
|
cm.indentLine(lineNo, delta);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta > 0) {
|
||||||
|
var spaces = Array(delta + 1).join(" ");
|
||||||
|
cm.replaceSelection(spaces);
|
||||||
|
} else {
|
||||||
|
var x = cm.getCursor().ch;
|
||||||
|
cm.replaceRange(
|
||||||
|
"",
|
||||||
|
{ line: lineNo, ch: x + delta },
|
||||||
|
{ line: lineNo, ch: x },
|
||||||
|
"+indent"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function indentAtCursor(cm, dx, stops) {
|
||||||
|
// Indent single line at cursor
|
||||||
|
var cursor = cm.getCursor();
|
||||||
|
var lineNo = cursor.line;
|
||||||
|
var x = cursor.ch;
|
||||||
|
var indent = getIndent(cm, cursor.line);
|
||||||
|
|
||||||
|
var stop = nextStop(stops, x, dx);
|
||||||
|
var useStops = indent == null || x === indent;
|
||||||
|
var nextX = stop != null && useStops ? stop : Math.max(0, x + dx * 2);
|
||||||
|
|
||||||
|
if (indent != null && indent < x && x < nextX) {
|
||||||
|
var spaces = Array(nextX - x + 1).join(" ");
|
||||||
|
cm.replaceSelection(spaces);
|
||||||
|
} else {
|
||||||
|
indentLine(cm, lineNo, nextX - x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTab(cm, dx) {
|
||||||
|
var hasSelection = cm.somethingSelected();
|
||||||
|
var state = ensureState(cm);
|
||||||
|
var stops = expandTabStops(state.tabStops);
|
||||||
|
|
||||||
|
if (hasSelection) {
|
||||||
|
indentSelection(cm, dx, stops);
|
||||||
|
} else {
|
||||||
|
indentAtCursor(cm, dx, stops);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Locus/Guides layer
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function getLayerContainer(cm) {
|
||||||
|
var wrapper = cm.getWrapperElement();
|
||||||
|
var lines = wrapper.querySelector(".CodeMirror-lines");
|
||||||
|
var container = lines.parentNode;
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parenSelected(paren, sel) {
|
||||||
|
return sel.contains({ line: paren.lineNo, ch: paren.x }) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointRevealsParenTrail(trail, pos) {
|
||||||
|
return (
|
||||||
|
pos.line === trail.lineNo &&
|
||||||
|
trail.startX <= pos.ch /* && cursor.ch <= trail.endX */
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideParen(cm, paren) {
|
||||||
|
var sel = cm.getDoc().sel;
|
||||||
|
var sel0 = sel.ranges[0];
|
||||||
|
var shouldShowCloser =
|
||||||
|
paren.lineNo === paren.closer.lineNo ||
|
||||||
|
!paren.closer.trail ||
|
||||||
|
pointRevealsParenTrail(paren.closer.trail, sel0.anchor) ||
|
||||||
|
pointRevealsParenTrail(paren.closer.trail, sel0.head) ||
|
||||||
|
parenSelected(paren.closer, sel);
|
||||||
|
|
||||||
|
if (!shouldShowCloser) {
|
||||||
|
addMark(
|
||||||
|
cm,
|
||||||
|
paren.closer.lineNo,
|
||||||
|
paren.closer.x,
|
||||||
|
paren.closer.x + 1,
|
||||||
|
CLASSNAME_LOCUS_PAREN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
hideParens(cm, paren.children);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideParens(cm, parens) {
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < parens.length; i++) {
|
||||||
|
hideParen(cm, parens[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function charPos(cm, paren) {
|
||||||
|
var p = cm.charCoords({ line: paren.lineNo, ch: paren.x }, "local");
|
||||||
|
var w = p.right - p.left;
|
||||||
|
return {
|
||||||
|
midx: p.left + w / 2,
|
||||||
|
right: p.right,
|
||||||
|
left: p.left,
|
||||||
|
top: p.top,
|
||||||
|
bottom: p.bottom,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRightBound(cm, startLine, endLine) {
|
||||||
|
var doc = cm.getDoc();
|
||||||
|
var maxWidth = 0;
|
||||||
|
var maxLineNo = 0;
|
||||||
|
var i;
|
||||||
|
for (i = startLine; i <= endLine; i++) {
|
||||||
|
var line = doc.getLine(i);
|
||||||
|
if (line.length > maxWidth) {
|
||||||
|
maxWidth = line.length;
|
||||||
|
maxLineNo = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var wall = charPos(cm, { lineNo: maxLineNo, x: maxWidth });
|
||||||
|
return wall.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addBox(cm, paren) {
|
||||||
|
var layer = cm[STATE_PROP].layer;
|
||||||
|
var paper = layer.paper;
|
||||||
|
var charW = layer.charW;
|
||||||
|
var charH = layer.charH;
|
||||||
|
|
||||||
|
var open = charPos(cm, paren);
|
||||||
|
var close = charPos(cm, paren.closer);
|
||||||
|
|
||||||
|
var r = 4;
|
||||||
|
|
||||||
|
if (paren.closer.trail && paren.lineNo !== paren.closer.lineNo) {
|
||||||
|
switch (layer.type) {
|
||||||
|
case "guides":
|
||||||
|
paper.path(
|
||||||
|
["M", open.midx, open.bottom, "V", close.bottom].join(" ")
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "locus":
|
||||||
|
var right = getRightBound(cm, paren.lineNo, paren.closer.lineNo);
|
||||||
|
paper.path(
|
||||||
|
[
|
||||||
|
"M",
|
||||||
|
open.midx,
|
||||||
|
open.top + r,
|
||||||
|
"A",
|
||||||
|
r,
|
||||||
|
r,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
open.midx + r,
|
||||||
|
open.top,
|
||||||
|
"H",
|
||||||
|
right - r,
|
||||||
|
"A",
|
||||||
|
r,
|
||||||
|
r,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
right,
|
||||||
|
open.top + r,
|
||||||
|
"V",
|
||||||
|
close.bottom,
|
||||||
|
"H",
|
||||||
|
open.midx,
|
||||||
|
"V",
|
||||||
|
open.bottom,
|
||||||
|
].join(" ")
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addBoxes(cm, paren.children);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addBoxes(cm, parens) {
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < parens.length; i++) {
|
||||||
|
addBox(cm, parens[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addLayer(cm, type) {
|
||||||
|
var layer = cm[STATE_PROP].layer;
|
||||||
|
layer.type = type;
|
||||||
|
|
||||||
|
var el = document.createElement("div");
|
||||||
|
el.style.position = "absolute";
|
||||||
|
el.style.left = "0";
|
||||||
|
el.style.top = "0";
|
||||||
|
el.style["z-index"] = 100;
|
||||||
|
el.className = CLASSNAME_LOCUS_LAYER;
|
||||||
|
|
||||||
|
layer.el = el;
|
||||||
|
layer.container.appendChild(el);
|
||||||
|
|
||||||
|
var pixelW = layer.container.clientWidth;
|
||||||
|
var pixelH = layer.container.clientHeight;
|
||||||
|
|
||||||
|
layer.paper = Raphael(el, pixelW, pixelH);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearLayer(cm) {
|
||||||
|
var layer = cm[STATE_PROP].layer;
|
||||||
|
if (layer && layer.el) {
|
||||||
|
layer.container.removeChild(layer.el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLocusLayer(cm, parens) {
|
||||||
|
clearMarks(cm, CLASSNAME_LOCUS_PAREN);
|
||||||
|
if (parens) {
|
||||||
|
hideParens(cm, parens);
|
||||||
|
clearLayer(cm);
|
||||||
|
// addLayer(cm, 'locus'); // don't draw boxes, just draw guides
|
||||||
|
addLayer(cm, "guides");
|
||||||
|
addBoxes(cm, parens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGuidesLayer(cm, parens) {
|
||||||
|
if (parens) {
|
||||||
|
clearLayer(cm);
|
||||||
|
addLayer(cm, "guides");
|
||||||
|
addBoxes(cm, parens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Text Correction
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// If `changes` is missing, then only the cursor position has changed.
|
||||||
|
function fixText(state, changes) {
|
||||||
|
// Get editor data
|
||||||
|
var cm = state.cm;
|
||||||
|
var text = cm.getValue();
|
||||||
|
var hasSelection = cm.somethingSelected();
|
||||||
|
var selections = cm.listSelections();
|
||||||
|
var cursor = cm.getCursor();
|
||||||
|
var scroller = cm.getScrollerElement();
|
||||||
|
|
||||||
|
// Create options
|
||||||
|
var options = {
|
||||||
|
cursorLine: cursor.line,
|
||||||
|
cursorX: cursor.ch,
|
||||||
|
prevCursorLine: state.prevCursorLine,
|
||||||
|
prevCursorX: state.prevCursorX,
|
||||||
|
};
|
||||||
|
if (hasSelection) {
|
||||||
|
options.selectionStartLine = getSelectionStartLine(cm);
|
||||||
|
}
|
||||||
|
if (state.options) {
|
||||||
|
var p;
|
||||||
|
for (p in state.options) {
|
||||||
|
if (state.options.hasOwnProperty(p)) {
|
||||||
|
options[p] = state.options[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changes) {
|
||||||
|
options.changes = convertChanges(changes);
|
||||||
|
}
|
||||||
|
|
||||||
|
var locus = state.options && state.options.locus;
|
||||||
|
var guides = state.options && state.options.guides;
|
||||||
|
|
||||||
|
if (locus || guides) {
|
||||||
|
delete options.locus;
|
||||||
|
delete options.guides;
|
||||||
|
options.returnParens = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run Parinfer
|
||||||
|
var result;
|
||||||
|
var mode = SMART_MODE;
|
||||||
|
let output = `Mode: ${mode}\n`;
|
||||||
|
|
||||||
|
output += `Before Text:\n${text}\n\n`;
|
||||||
|
output += `Input Options:\n${JSON.stringify(options, null, 2)}\n\n`;
|
||||||
|
switch (mode) {
|
||||||
|
case INDENT_MODE:
|
||||||
|
result = parinfer.indentMode(text, options);
|
||||||
|
break;
|
||||||
|
case PAREN_MODE:
|
||||||
|
result = parinfer.parenMode(text, options);
|
||||||
|
break;
|
||||||
|
case SMART_MODE:
|
||||||
|
result = parinfer.smartMode(text, options);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ensureMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
output += `After Text:\n${result.text}\n\n`;
|
||||||
|
output += `Result:\n${JSON.stringify(result, null, 2)}\n\n`;
|
||||||
|
document.getElementById("output").innerHTML = output;
|
||||||
|
|
||||||
|
// Remember the paren tree.
|
||||||
|
state.parens = result.parens;
|
||||||
|
|
||||||
|
// Remember tab stops for smart tabbing.
|
||||||
|
state.tabStops = result.tabStops;
|
||||||
|
|
||||||
|
if (text !== result.text) {
|
||||||
|
// Backup history
|
||||||
|
var hist = cm.getHistory();
|
||||||
|
|
||||||
|
// Update text
|
||||||
|
cm.setValue(result.text);
|
||||||
|
|
||||||
|
// Update cursor and selection
|
||||||
|
state.monitorCursor = false;
|
||||||
|
if (hasSelection) {
|
||||||
|
cm.setSelections(selections);
|
||||||
|
} else {
|
||||||
|
cm.setCursor(result.cursorLine, result.cursorX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore history to avoid pushing our edits to the history stack.
|
||||||
|
cm.setHistory(hist);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
state.monitorCursor = true;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
// Update scroll position
|
||||||
|
cm.scrollTo(scroller.scrollLeft, scroller.scrollTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear or add new marks
|
||||||
|
updateErrorMarks(cm, result.error);
|
||||||
|
updateParenTrailMarks(cm, result.parenTrails);
|
||||||
|
|
||||||
|
// Remember the cursor position for next time
|
||||||
|
state.prevCursorLine = result.cursorLine;
|
||||||
|
state.prevCursorX = result.cursorX;
|
||||||
|
|
||||||
|
if (locus) {
|
||||||
|
updateLocusLayer(cm, result.parens);
|
||||||
|
} else if (guides) {
|
||||||
|
updateGuidesLayer(cm, result.parens);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-run with original mode if code was finally fixed in Paren Mode.
|
||||||
|
if (state.fixMode && result.success) {
|
||||||
|
state.fixMode = false;
|
||||||
|
return fixText(state, changes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.success;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// CodeMirror Integration
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function onCursorChange(state) {
|
||||||
|
clearTimeout(state.cursorTimeout);
|
||||||
|
if (state.monitorCursor) {
|
||||||
|
state.cursorTimeout = setTimeout(function () {
|
||||||
|
fixText(state);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTextChanges(state, changes) {
|
||||||
|
clearTimeout(state.cursorTimeout);
|
||||||
|
var origin = changes[0].origin;
|
||||||
|
if (origin !== "setValue") {
|
||||||
|
fixText(state, changes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function on(state) {
|
||||||
|
if (state.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.callbackCursor = function (cm) {
|
||||||
|
onCursorChange(state);
|
||||||
|
};
|
||||||
|
state.callbackChanges = function (cm, changes) {
|
||||||
|
onTextChanges(state, changes);
|
||||||
|
};
|
||||||
|
var cm = state.cm;
|
||||||
|
cm.on("cursorActivity", state.callbackCursor);
|
||||||
|
cm.on("changes", state.callbackChanges);
|
||||||
|
state.parinferKeys = {
|
||||||
|
Tab: function (cm) {
|
||||||
|
onTab(cm, 1);
|
||||||
|
},
|
||||||
|
"Shift-Tab": function (cm) {
|
||||||
|
onTab(cm, -1);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cm.addKeyMap(state.parinferKeys);
|
||||||
|
state.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function off(state) {
|
||||||
|
if (!state.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var cm = state.cm;
|
||||||
|
clearAllMarks(cm);
|
||||||
|
cm.off("cursorActivity", state.callbackCursor);
|
||||||
|
cm.off("changes", state.callbackChanges);
|
||||||
|
cm.removeKeyMap(state.parinferKeys);
|
||||||
|
state.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Public API
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function init(cm, mode, options) {
|
||||||
|
var state = cm[STATE_PROP];
|
||||||
|
if (state) {
|
||||||
|
throw error("init has already been called on this CodeMirror instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
mode = mode || SMART_MODE;
|
||||||
|
ensureMode(mode);
|
||||||
|
|
||||||
|
state = initialState(cm, mode, options);
|
||||||
|
cm[STATE_PROP] = state;
|
||||||
|
|
||||||
|
state.layer = {
|
||||||
|
container: getLayerContainer(cm),
|
||||||
|
};
|
||||||
|
return enable(cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enable(cm) {
|
||||||
|
var state = ensureState(cm);
|
||||||
|
|
||||||
|
// preprocess text to keep Parinfer from changing code structure
|
||||||
|
if (state.mode !== PAREN_MODE) {
|
||||||
|
state.fixMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
on(state);
|
||||||
|
return fixText(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable(cm) {
|
||||||
|
var state = ensureState(cm);
|
||||||
|
off(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMode(cm, mode) {
|
||||||
|
var state = ensureState(cm);
|
||||||
|
ensureMode(mode);
|
||||||
|
state.mode = mode;
|
||||||
|
return fixText(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setOptions(cm, options) {
|
||||||
|
var state = ensureState(cm);
|
||||||
|
state.options = options;
|
||||||
|
return fixText(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
var API = {
|
||||||
|
version: "1.4.1",
|
||||||
|
init: init,
|
||||||
|
enable: enable,
|
||||||
|
disable: disable,
|
||||||
|
setMode: setMode,
|
||||||
|
setOptions: setOptions,
|
||||||
|
};
|
||||||
|
|
||||||
|
return API;
|
||||||
|
}); // end module anonymous scope
|
1622
docs/parinfer-tester/assets/parinfer.js
Normal file
1622
docs/parinfer-tester/assets/parinfer.js
Normal file
File diff suppressed because it is too large
Load diff
646
docs/parinfer-tester/assets/style.css
Normal file
646
docs/parinfer-tester/assets/style.css
Normal file
|
@ -0,0 +1,646 @@
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Open Sans", Arial, freesans, sans-serif;
|
||||||
|
color: #555;
|
||||||
|
background: #f7f7f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#xkcd a {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#xkcd .caption {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc {
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 10px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.header-link {
|
||||||
|
color: #ccc;
|
||||||
|
position: absolute;
|
||||||
|
left: -1em;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-link a {
|
||||||
|
border-bottom: 0;
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-link {
|
||||||
|
line-height: 1.6em;
|
||||||
|
opacity: 0.4;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-link.toc-active {
|
||||||
|
border-right: 2px solid #333;
|
||||||
|
margin-right: 0px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-link.toc-active-ancestor {
|
||||||
|
border-right: 2px solid #ccc;
|
||||||
|
margin-right: 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-level-1 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-level-2 {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-level-3 {
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-level-4 {
|
||||||
|
padding-left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#controls {
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
background-color: rgba(0, 255, 255, 0.3);
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interact {
|
||||||
|
padding: 20px;
|
||||||
|
background: #e7eae9;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 32px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interact img {
|
||||||
|
float: left;
|
||||||
|
height: 58px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interact:after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paredit-table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paredit-table th,
|
||||||
|
.paredit-table td {
|
||||||
|
padding: 14px;
|
||||||
|
border: 1px solid #cecece;
|
||||||
|
background: #fefefe;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paredit-table th {
|
||||||
|
color: #888;
|
||||||
|
font-style: italic;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
color: #222;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 32px 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 48px;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 em {
|
||||||
|
text-decoration: none;
|
||||||
|
font-style: normal;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: -18px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle em {
|
||||||
|
color: #111;
|
||||||
|
border-bottom: 1px solid #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features {
|
||||||
|
color: #888;
|
||||||
|
list-style-type: square;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
border-bottom: 1px solid #cc3385;
|
||||||
|
color: #cc3385;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link,
|
||||||
|
a:visited,
|
||||||
|
a:hover,
|
||||||
|
a:active {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
li {
|
||||||
|
line-height: 1.7em;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 300px;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-bottom: 32px;
|
||||||
|
height: 100%;
|
||||||
|
border-right: 1px solid #ddd;
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-logo {
|
||||||
|
margin: 60px 0 0 70px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credits {
|
||||||
|
font-size: 0.8em;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: -20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credits a {
|
||||||
|
opacity: 0.5;
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
position: absolute;
|
||||||
|
left: 300px;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
right: 0;
|
||||||
|
margin: 0;
|
||||||
|
border-top: 1px solid #dedede;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper.fold {
|
||||||
|
background: #fefefe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper.fold .interact {
|
||||||
|
background: #f7f7f7;
|
||||||
|
border: 1px solid #dedede;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper.fold .title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
width: 862px;
|
||||||
|
padding: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper:first-child > section {
|
||||||
|
margin-top: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.caption {
|
||||||
|
padding-top: 20px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
margin-left: 5px;
|
||||||
|
font-style: italic;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: Menlo, Consolas, "DejaVu Sans Mono", monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
font-family: Menlo, Consolas, "DejaVu Sans Mono", monospace;
|
||||||
|
background: #fefefe;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
padding: 5px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-bracket.cm-eol {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-col {
|
||||||
|
}
|
||||||
|
|
||||||
|
.col {
|
||||||
|
float: left;
|
||||||
|
width: 350px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-col:after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-intro {
|
||||||
|
height: 200px;
|
||||||
|
margin-bottom: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-lisp-expr,
|
||||||
|
#cm-code-c-expr {
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-lisp-indent,
|
||||||
|
#cm-code-c-indent {
|
||||||
|
height: 174px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-skim,
|
||||||
|
#cm-code-inspect {
|
||||||
|
height: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-skim span.cm-bracket {
|
||||||
|
opacity: 0.15;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-inspect span.cm-keyword,
|
||||||
|
#cm-code-inspect span.cm-variable,
|
||||||
|
#cm-code-inspect span.cm-builtin,
|
||||||
|
#cm-code-inspect span.cm-string,
|
||||||
|
#cm-code-inspect span.cm-def,
|
||||||
|
#cm-code-inspect span.cm-atom,
|
||||||
|
#cm-code-inspect span.cm-number,
|
||||||
|
#cm-code-inspect span.cm-comment {
|
||||||
|
opacity: 0.15;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-indent,
|
||||||
|
#cm-code-indent-far {
|
||||||
|
height: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-indent-multi,
|
||||||
|
#cm-code-intro-indent {
|
||||||
|
height: 130px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-line,
|
||||||
|
#cm-code-intro-insert {
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-comment,
|
||||||
|
#cm-code-intro-comment {
|
||||||
|
height: 130px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-wrap,
|
||||||
|
#cm-code-splice,
|
||||||
|
#cm-code-slurp,
|
||||||
|
#cm-code-barf {
|
||||||
|
height: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-string {
|
||||||
|
height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-displaced,
|
||||||
|
#cm-code-not-displaced {
|
||||||
|
height: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-indent-input,
|
||||||
|
#cm-code-indent-output {
|
||||||
|
height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-indent-output {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-indent-output .cm-bracket.cm-eol {
|
||||||
|
opacity: 1;
|
||||||
|
background: #aaffc2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-indent-input .cm-bracket.cm-eol,
|
||||||
|
#cm-code-indent-input .cm-bracket.cm-sol {
|
||||||
|
opacity: 1;
|
||||||
|
background: #ffbebe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.removed {
|
||||||
|
background: #ffbebe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kept {
|
||||||
|
background: #ffe8b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inserted {
|
||||||
|
background: #aaffc2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-indent-output .cm-bracket.cm-mol,
|
||||||
|
#cm-code-indent-input .cm-bracket.cm-mol {
|
||||||
|
background: #ffe8b0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-indent-output span.cm-keyword,
|
||||||
|
#cm-code-indent-output span.cm-variable,
|
||||||
|
#cm-code-indent-output span.cm-builtin,
|
||||||
|
#cm-code-indent-output span.cm-string,
|
||||||
|
#cm-code-indent-output span.cm-def,
|
||||||
|
#cm-code-indent-output span.cm-atom,
|
||||||
|
#cm-code-indent-output span.cm-number,
|
||||||
|
#cm-code-indent-output span.cm-comment,
|
||||||
|
#cm-code-indent-input span.cm-keyword,
|
||||||
|
#cm-code-indent-input span.cm-variable,
|
||||||
|
#cm-code-indent-input span.cm-builtin,
|
||||||
|
#cm-code-indent-input span.cm-string,
|
||||||
|
#cm-code-indent-input span.cm-def,
|
||||||
|
#cm-code-indent-input span.cm-atom,
|
||||||
|
#cm-code-indent-input span.cm-number,
|
||||||
|
#cm-code-indent-input span.cm-comment {
|
||||||
|
opacity: 0.3;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-warn-good,
|
||||||
|
#cm-code-warn-bad {
|
||||||
|
font-size: 14px;
|
||||||
|
height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-enter {
|
||||||
|
height: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-paren-tune,
|
||||||
|
#cm-code-paren-frac,
|
||||||
|
#cm-code-paren-comment,
|
||||||
|
#cm-code-paren-wrap,
|
||||||
|
#cm-code-intro-paren {
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-intro-paredit {
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-displaced-after-balance,
|
||||||
|
#cm-code-not-displaced-on-enter,
|
||||||
|
#cm-code-displaced-after-cursor-leaves {
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.centered {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.img-link {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote.aside {
|
||||||
|
border-left: 5px solid #ddd;
|
||||||
|
margin: 0;
|
||||||
|
margin-top: 60px;
|
||||||
|
padding-left: 20px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-point {
|
||||||
|
font-style: italic;
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
td .side-point {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #ffff7b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-title {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-body {
|
||||||
|
margin-left: 34px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green {
|
||||||
|
color: #63ca63;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red {
|
||||||
|
color: #e84545;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question i.fa {
|
||||||
|
color: #5380c7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer {
|
||||||
|
margin-left: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.fa {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cm-code-editor {
|
||||||
|
height: auto;
|
||||||
|
float: left;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#debug-state {
|
||||||
|
overflow-x: auto;
|
||||||
|
width: 50%;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-table {
|
||||||
|
margin-top: 6px;
|
||||||
|
font: 14px Menlo;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-table td {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0px 10px;
|
||||||
|
height: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-table .line-no {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-table .line-dy {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-table .active-line {
|
||||||
|
background: #ff0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gear {
|
||||||
|
opacity: 0.3;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gear.powered {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gear.auto-indent-gear {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gear.auto-indent-gear.invisible {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parinfer-gear {
|
||||||
|
opacity: 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gearbox {
|
||||||
|
margin-bottom: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lego-img {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.math-page {
|
||||||
|
background: #fefefe;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
padding: 30px;
|
||||||
|
font-family: "Computer Modern Serif";
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.math-page ul li {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.math-page li {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.where-x {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.math-page .MathJax {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.math-page .side-note {
|
||||||
|
float: right;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.only-mobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 850px) {
|
||||||
|
#app {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
width: 782px;
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.credits {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.only-mobile {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
99
docs/parinfer-tester/assets/theme.css
Normal file
99
docs/parinfer-tester/assets/theme.css
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/* DEFAULT THEME */
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
.cm-s-default .cm-header {color: blue;}
|
||||||
|
.cm-s-default .cm-quote {color: #090;}
|
||||||
|
.cm-negative {color: #d44;}
|
||||||
|
.cm-positive {color: #292;}
|
||||||
|
.cm-header, .cm-strong {font-weight: bold;}
|
||||||
|
.cm-em {font-style: italic;}
|
||||||
|
.cm-link {text-decoration: underline;}
|
||||||
|
.cm-strikethrough {text-decoration: line-through;}
|
||||||
|
|
||||||
|
.cm-s-default .cm-keyword {color: #708;}
|
||||||
|
.cm-s-default .cm-atom {color: #219;}
|
||||||
|
.cm-s-default .cm-number {color: #164;}
|
||||||
|
.cm-s-default .cm-def {color: #00f;}
|
||||||
|
.cm-s-default .cm-variable,
|
||||||
|
.cm-s-default .cm-punctuation,
|
||||||
|
.cm-s-default .cm-property,
|
||||||
|
.cm-s-default .cm-operator {}
|
||||||
|
.cm-s-default .cm-variable-2 {color: #05a;}
|
||||||
|
.cm-s-default .cm-variable-3 {color: #085;}
|
||||||
|
.cm-s-default .cm-comment {color: #a50;}
|
||||||
|
.cm-s-default .cm-string {color: #a11;}
|
||||||
|
.cm-s-default .cm-string-2 {color: #f50;}gt
|
||||||
|
.cm-s-default .cm-meta {color: #555;}
|
||||||
|
.cm-s-default .cm-qualifier {color: #555;}
|
||||||
|
.cm-s-default .cm-builtin {color: #30a;}
|
||||||
|
.cm-s-default .cm-bracket {color: #997;}
|
||||||
|
.cm-s-default .cm-tag {color: #170;}
|
||||||
|
.cm-s-default .cm-attribute {color: #00c;}
|
||||||
|
.cm-s-default .cm-hr {color: #999;}
|
||||||
|
.cm-s-default .cm-link {color: #00c;}
|
||||||
|
|
||||||
|
.cm-s-default .cm-error {color: #f00;}
|
||||||
|
.cm-invalidchar {color: #f00;}
|
||||||
|
|
||||||
|
.CodeMirror-composing { border-bottom: 2px solid; }
|
||||||
|
|
||||||
|
/* Default styles for common addons */
|
||||||
|
|
||||||
|
/*
|
||||||
|
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
|
||||||
|
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
||||||
|
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
|
||||||
|
.CodeMirror-activeline-background {background: #e8f2ff;}
|
||||||
|
|
||||||
|
.CodeMirror-cursor {
|
||||||
|
border-left: 1px solid black;
|
||||||
|
border-right: none;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
.cm-s-github .cm-keyword {
|
||||||
|
color: #a71d5d;
|
||||||
|
} /* clojure core forms */
|
||||||
|
.cm-s-github .cm-builtin {
|
||||||
|
color: #a71d5d;
|
||||||
|
} /* clojure core library */
|
||||||
|
.cm-s-github .cm-string {
|
||||||
|
color: #183691;
|
||||||
|
} /* clojure strings */
|
||||||
|
.cm-s-github .cm-variable {
|
||||||
|
color: #333;
|
||||||
|
} /* clojure misc symbols */
|
||||||
|
.cm-s-github .cm-def {
|
||||||
|
color: #795da3;
|
||||||
|
} /* clojure function calls or defs */
|
||||||
|
.cm-s-github .cm-atom {
|
||||||
|
color: #0086b3;
|
||||||
|
} /* clojure keyword, bool, nil */
|
||||||
|
.cm-s-github .cm-number {
|
||||||
|
color: #0086b3;
|
||||||
|
} /* clojure number */
|
||||||
|
.cm-s-github .cm-bracket {
|
||||||
|
color: #333;
|
||||||
|
} /* clojure brackets */
|
||||||
|
.cm-s-github .cm-comment {
|
||||||
|
color: #969896;
|
||||||
|
font-style: italic;
|
||||||
|
} /* clojure comment */
|
||||||
|
|
||||||
|
div.CodeMirror span.CodeMirror-matchingbracket {
|
||||||
|
background: #e8f2ff;
|
||||||
|
color: inherit;
|
||||||
|
border-bottom: 1px solid #111;
|
||||||
|
}
|
||||||
|
div.CodeMirror span.CodeMirror-nonmatchingbracket {
|
||||||
|
}
|
||||||
|
|
||||||
|
/* don't change selection color based on an editor's focus status. */
|
||||||
|
.CodeMirror-selected {
|
||||||
|
background: #d7d4f0; /* #d9d9d9 */
|
||||||
|
}
|
||||||
|
.CodeMirror-focused .CodeMirror-selected {
|
||||||
|
background: #d7d4f0;
|
||||||
|
}
|
32
docs/parinfer-tester/index.html
Normal file
32
docs/parinfer-tester/index.html
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||||
|
<title>Parinfer - Tester</title>
|
||||||
|
<meta name="description" content="Parinfer - simpler Lisp editing" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="stylesheet" href="./assets/codemirror.css" />
|
||||||
|
<link rel="stylesheet" href="./assets/style.css" />
|
||||||
|
<link rel="stylesheet" href="./assets/theme.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="no-mathjax">
|
||||||
|
<textarea id="code-indent"></textarea>
|
||||||
|
|
||||||
|
<textarea id="output" cols="80" rows="50"></textarea>
|
||||||
|
|
||||||
|
<div id="controls-container"><noscript data-reactid=".1"></noscript></div>
|
||||||
|
|
||||||
|
<script src="./assets/codemirror.js"></script>
|
||||||
|
<script src="./assets/parinfer.js"></script>
|
||||||
|
<script src="./assets/parinfer-codemirror.js"></script>
|
||||||
|
<script>
|
||||||
|
var myCodeMirror = CodeMirror.fromTextArea(
|
||||||
|
document.getElementById("code-indent")
|
||||||
|
);
|
||||||
|
parinferCodeMirror.init(myCodeMirror);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
5421
package-lock.json
generated
5421
package-lock.json
generated
File diff suppressed because it is too large
Load diff
23
package.json
23
package.json
|
@ -42,6 +42,7 @@
|
||||||
"comment-json": "^4.2.3",
|
"comment-json": "^4.2.3",
|
||||||
"follow-redirects": "^1.15.2",
|
"follow-redirects": "^1.15.2",
|
||||||
"glob": "^8.0.3",
|
"glob": "^8.0.3",
|
||||||
|
"parinfer": "^3.13.1",
|
||||||
"vscode-languageclient": "^8.0.1"
|
"vscode-languageclient": "^8.0.1"
|
||||||
},
|
},
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
|
@ -171,6 +172,28 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"configuration": [
|
"configuration": [
|
||||||
|
{
|
||||||
|
"id": "opengoal-goal",
|
||||||
|
"title": "GOAL Tooling",
|
||||||
|
"properties": {
|
||||||
|
"opengoal.parinferMode": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "DISABLED",
|
||||||
|
"enum": [
|
||||||
|
"DISABLED",
|
||||||
|
"SMART",
|
||||||
|
"PAREN",
|
||||||
|
"INDENT"
|
||||||
|
],
|
||||||
|
"enumDescriptions": [
|
||||||
|
"Disable parinfer",
|
||||||
|
"Parinfer will try it's best to deduce when to run Paren mode and when to run Indent mode",
|
||||||
|
"Parinfer will handle the indentation, you handle the parens",
|
||||||
|
"Parinfer will handle the parens, you handle the indentation"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "opengoal-decomp",
|
"id": "opengoal-decomp",
|
||||||
"title": "Decompilation",
|
"title": "Decompilation",
|
||||||
|
|
|
@ -4,6 +4,7 @@ export function getConfig() {
|
||||||
const configOptions = vscode.workspace.getConfiguration("opengoal");
|
const configOptions = vscode.workspace.getConfiguration("opengoal");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
opengoalParinferMode: configOptions.get<string>("parinferMode"),
|
||||||
launchLspOnStartup: configOptions.get<boolean>("launchLspOnStartup"),
|
launchLspOnStartup: configOptions.get<boolean>("launchLspOnStartup"),
|
||||||
opengoalLspVersion: configOptions.get<string>("opengoalLspVersion"),
|
opengoalLspVersion: configOptions.get<string>("opengoalLspVersion"),
|
||||||
opengoalLspPath: configOptions.get<string>("opengoalLspPath"),
|
opengoalLspPath: configOptions.get<string>("opengoalLspPath"),
|
||||||
|
@ -109,3 +110,12 @@ export async function updateJak2DecompConfigDirectory(dir: string) {
|
||||||
vscode.ConfigurationTarget.Global
|
vscode.ConfigurationTarget.Global
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function updateOpengoalParinferMode(mode: string) {
|
||||||
|
const userConfig = vscode.workspace.getConfiguration();
|
||||||
|
await userConfig.update(
|
||||||
|
"opengoal.parinferMode",
|
||||||
|
mode,
|
||||||
|
vscode.ConfigurationTarget.Global
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,11 @@ import { IRInlayHintsProvider } from "./languages/ir2/ir2-inlay-hinter";
|
||||||
import { OpenGOALDisasmRenameProvider } from "./languages/opengoal/disasm/opengoal-disasm-renamer";
|
import { OpenGOALDisasmRenameProvider } from "./languages/opengoal/disasm/opengoal-disasm-renamer";
|
||||||
import { activateMiscDecompTools } from "./decomp/misc-tools";
|
import { activateMiscDecompTools } from "./decomp/misc-tools";
|
||||||
import { IR2RenameProvider } from "./languages/ir2/ir2-renamer";
|
import { IR2RenameProvider } from "./languages/ir2/ir2-renamer";
|
||||||
|
import {
|
||||||
|
onChangeSelection,
|
||||||
|
onChangeTextDocument,
|
||||||
|
registerParinferCommands,
|
||||||
|
} from "./goal/parinfer/parinfer";
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext) {
|
export async function activate(context: vscode.ExtensionContext) {
|
||||||
try {
|
try {
|
||||||
|
@ -75,6 +80,11 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||||
|
|
||||||
// Start the LSP
|
// Start the LSP
|
||||||
lsp.activate(context);
|
lsp.activate(context);
|
||||||
|
|
||||||
|
// Parinfer
|
||||||
|
registerParinferCommands(context);
|
||||||
|
vscode.workspace.onDidChangeTextDocument(onChangeTextDocument);
|
||||||
|
vscode.window.onDidChangeTextEditorSelection(onChangeSelection);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
vscode.window.showErrorMessage(
|
vscode.window.showErrorMessage(
|
||||||
"Failed to activate OpenGOAL extension, see logs for details"
|
"Failed to activate OpenGOAL extension, see logs for details"
|
||||||
|
|
346
src/goal/parinfer/parinfer.ts
Normal file
346
src/goal/parinfer/parinfer.ts
Normal file
|
@ -0,0 +1,346 @@
|
||||||
|
import { indentMode, smartMode, parenMode } from "parinfer";
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
import { integer } from "vscode-languageclient";
|
||||||
|
import { getConfig, updateOpengoalParinferMode } from "../../config/config";
|
||||||
|
import { getEditorRange } from "../../utils/editor-utils";
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// - iron out some quirks around undoing
|
||||||
|
// - tab stops
|
||||||
|
// - highlight errors
|
||||||
|
// - initial paren mode should also trim empty lines
|
||||||
|
|
||||||
|
const opacityDecoration = vscode.window.createTextEditorDecorationType({
|
||||||
|
opacity: "0.6",
|
||||||
|
});
|
||||||
|
|
||||||
|
function parinferRangeToVSCodeRange(parenTrail: any) {
|
||||||
|
return new vscode.Range(
|
||||||
|
parenTrail.lineNo,
|
||||||
|
parenTrail.startX,
|
||||||
|
parenTrail.lineNo,
|
||||||
|
parenTrail.endX
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dimParenTrails(editor: vscode.TextEditor, parenTrails: any) {
|
||||||
|
const parenTrailsRanges = parenTrails.map(parinferRangeToVSCodeRange);
|
||||||
|
editor.setDecorations(opacityDecoration, parenTrailsRanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateParenTrails(editor: vscode.TextEditor, parenTrails: any) {
|
||||||
|
dimParenTrails(editor, parenTrails);
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ParinferMode {
|
||||||
|
DISABLED = "DISABLED",
|
||||||
|
INDENT = "INDENT",
|
||||||
|
PAREN = "PAREN",
|
||||||
|
SMART = "SMART",
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EventQueueItemType {
|
||||||
|
TEXT_CHANGED = "TEXT_CHANGED",
|
||||||
|
SELECTION_CHANGED = "SELECTION_CHANGED",
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParinferChangeEntry {
|
||||||
|
lineNo: integer;
|
||||||
|
x: integer;
|
||||||
|
oldText: string;
|
||||||
|
newText: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventQueueItem {
|
||||||
|
type: EventQueueItemType;
|
||||||
|
text: string;
|
||||||
|
cursorLine?: integer;
|
||||||
|
cursorX?: integer;
|
||||||
|
changes?: ParinferChangeEntry[];
|
||||||
|
prevCursorLine?: integer;
|
||||||
|
prevCursorX?: integer;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParinferOptions {
|
||||||
|
cursorLine: integer;
|
||||||
|
cursorX: integer;
|
||||||
|
prevCursorLine?: integer;
|
||||||
|
prevCursorX?: integer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventQueue: EventQueueItem[] = [];
|
||||||
|
const maxEventQueueSize = 10;
|
||||||
|
let currentParinferMode = ParinferMode.DISABLED;
|
||||||
|
|
||||||
|
const parinferStatusItem = vscode.window.createStatusBarItem(
|
||||||
|
vscode.StatusBarAlignment.Left,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
function updateStatus() {
|
||||||
|
switch (currentParinferMode) {
|
||||||
|
case ParinferMode.DISABLED:
|
||||||
|
parinferStatusItem.text = "$(json) Parinfer Disabled";
|
||||||
|
parinferStatusItem.tooltip = "Currently doing nothing! - Change Mode";
|
||||||
|
parinferStatusItem.command = "opengoal.parinfer.changeMode";
|
||||||
|
break;
|
||||||
|
case ParinferMode.SMART:
|
||||||
|
parinferStatusItem.text = "$(json) Smart Parinfer";
|
||||||
|
parinferStatusItem.tooltip =
|
||||||
|
"Automatically runs in Paren or Indent mode - Change Mode";
|
||||||
|
parinferStatusItem.command = "opengoal.parinfer.changeMode";
|
||||||
|
break;
|
||||||
|
case ParinferMode.PAREN:
|
||||||
|
parinferStatusItem.text = "$(json) Paren Parinfer";
|
||||||
|
parinferStatusItem.tooltip =
|
||||||
|
"You handle the parens while parinfer handles indentation! - Change Mode";
|
||||||
|
parinferStatusItem.command = "opengoal.parinfer.changeMode";
|
||||||
|
break;
|
||||||
|
case ParinferMode.INDENT:
|
||||||
|
parinferStatusItem.text = "$(json) Parinfer Disabled";
|
||||||
|
parinferStatusItem.tooltip =
|
||||||
|
"You handle the indentation while parinfer handles the parens! - Change Mode";
|
||||||
|
parinferStatusItem.command = "opengoal.parinfer.changeMode";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeParinferMode(mode: ParinferMode) {
|
||||||
|
currentParinferMode = mode;
|
||||||
|
await updateOpengoalParinferMode(mode);
|
||||||
|
updateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanUpEventQueue() {
|
||||||
|
if (eventQueue.length > maxEventQueueSize) {
|
||||||
|
eventQueue.length = maxEventQueueSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(cleanUpEventQueue, 5 * 1000);
|
||||||
|
|
||||||
|
function applyParinfer(
|
||||||
|
editor: vscode.TextEditor,
|
||||||
|
text: string,
|
||||||
|
options: ParinferOptions,
|
||||||
|
mode: ParinferMode
|
||||||
|
) {
|
||||||
|
// console.log(`Options Before - ${JSON.stringify(options, null, 2)}`);
|
||||||
|
let parinferResult: any; // TODO - make a type def for this
|
||||||
|
if (mode === ParinferMode.DISABLED) {
|
||||||
|
return;
|
||||||
|
} else if (mode === ParinferMode.PAREN) {
|
||||||
|
parinferResult = parenMode(text, options);
|
||||||
|
} else if (mode === ParinferMode.INDENT) {
|
||||||
|
parinferResult = indentMode(text, options);
|
||||||
|
} else if (mode === ParinferMode.SMART) {
|
||||||
|
parinferResult = smartMode(text, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - clear decorations when text is removed
|
||||||
|
|
||||||
|
if (text === parinferResult.text) {
|
||||||
|
updateParenTrails(editor, parinferResult.parenTrails);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor
|
||||||
|
.edit(
|
||||||
|
(selectedText) => {
|
||||||
|
selectedText.replace(getEditorRange(editor), parinferResult.text);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
undoStopAfter: false,
|
||||||
|
undoStopBefore: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(function (editWasApplied) {
|
||||||
|
if (editWasApplied) {
|
||||||
|
// set the new cursor position
|
||||||
|
const newCursorPosition = new vscode.Position(
|
||||||
|
parinferResult.cursorLine,
|
||||||
|
parinferResult.cursorX
|
||||||
|
);
|
||||||
|
const nextCursor = new vscode.Selection(
|
||||||
|
newCursorPosition,
|
||||||
|
newCursorPosition
|
||||||
|
);
|
||||||
|
editor.selection = nextCursor;
|
||||||
|
updateParenTrails(editor, parinferResult.parenTrails);
|
||||||
|
} else {
|
||||||
|
// TODO: should we do something here if the edit fails?
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function processEventQueue() {
|
||||||
|
if (
|
||||||
|
eventQueue.length === 0 ||
|
||||||
|
eventQueue[0].type !== EventQueueItemType.SELECTION_CHANGED
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - wire this up to an event listener too (what order though!)
|
||||||
|
const activeEditor = vscode.window.activeTextEditor;
|
||||||
|
if (activeEditor === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectionEvent = eventQueue[0];
|
||||||
|
if (
|
||||||
|
selectionEvent.cursorLine === undefined ||
|
||||||
|
selectionEvent.cursorX === undefined
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const options: ParinferOptions = {
|
||||||
|
cursorLine: selectionEvent.cursorLine,
|
||||||
|
cursorX: selectionEvent.cursorX,
|
||||||
|
};
|
||||||
|
|
||||||
|
// check the last two events for previous cursor information
|
||||||
|
// TODO - not a huge fan of this code -- prevCursor info is only on selectionEvents
|
||||||
|
// depending on queue ordering is kinda sketchy though
|
||||||
|
if (eventQueue[1] && eventQueue[1].cursorLine !== undefined) {
|
||||||
|
options.prevCursorLine = eventQueue[1].cursorLine;
|
||||||
|
options.prevCursorX = eventQueue[1].cursorX;
|
||||||
|
} else if (eventQueue[2] && eventQueue[2].cursorLine !== undefined) {
|
||||||
|
options.prevCursorLine = eventQueue[2].cursorLine;
|
||||||
|
options.prevCursorX = eventQueue[2].cursorX;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Document change events happen first, then selection change events
|
||||||
|
// In otherwords, they should always be in pairs
|
||||||
|
applyParinfer(
|
||||||
|
activeEditor,
|
||||||
|
selectionEvent.text,
|
||||||
|
options,
|
||||||
|
currentParinferMode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onChangeSelection(
|
||||||
|
event: vscode.TextEditorSelectionChangeEvent
|
||||||
|
) {
|
||||||
|
const editor = event.textEditor;
|
||||||
|
|
||||||
|
if (editor.document.languageId !== "opengoal") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection = event.selections[0];
|
||||||
|
|
||||||
|
const newQueueEntry: EventQueueItem = {
|
||||||
|
type: EventQueueItemType.SELECTION_CHANGED,
|
||||||
|
text: editor.document.getText(),
|
||||||
|
cursorLine: selection.active.line,
|
||||||
|
cursorX: selection.active.character,
|
||||||
|
};
|
||||||
|
|
||||||
|
eventQueue.unshift(newQueueEntry);
|
||||||
|
processEventQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTextFromRange(txt: string, range: vscode.Range, length: integer) {
|
||||||
|
if (length === 0) return "";
|
||||||
|
|
||||||
|
const firstLine = range.start.line;
|
||||||
|
const firstChar = range.start.character;
|
||||||
|
|
||||||
|
const lines = txt.split("\n");
|
||||||
|
const line = lines[firstLine];
|
||||||
|
|
||||||
|
return line.substring(firstChar, firstChar + length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertChangeObjects(
|
||||||
|
oldText: string,
|
||||||
|
changeEvent: vscode.TextDocumentContentChangeEvent
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
lineNo: changeEvent.range.start.line,
|
||||||
|
newText: changeEvent.text,
|
||||||
|
oldText: getTextFromRange(
|
||||||
|
oldText,
|
||||||
|
changeEvent.range,
|
||||||
|
changeEvent.rangeLength
|
||||||
|
),
|
||||||
|
x: changeEvent.range.start.character,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function onChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||||
|
// drop any events that do not contain document changes
|
||||||
|
// (usually the first event to a document)
|
||||||
|
if (event.contentChanges && event.contentChanges.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.document.languageId !== "opengoal") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newQueueEntry: EventQueueItem = {
|
||||||
|
type: EventQueueItemType.TEXT_CHANGED,
|
||||||
|
text: event.document.getText(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// only create a "changes" property if we have a prior event
|
||||||
|
if (eventQueue[0] && eventQueue[0].text) {
|
||||||
|
const prevText = eventQueue[0].text;
|
||||||
|
const convertFn = convertChangeObjects.bind(null, prevText);
|
||||||
|
newQueueEntry.changes = event.contentChanges.map(convertFn);
|
||||||
|
}
|
||||||
|
|
||||||
|
eventQueue.unshift(newQueueEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showChangeModeMenu() {
|
||||||
|
const items: vscode.QuickPickItem[] = [
|
||||||
|
{
|
||||||
|
label: ParinferMode.DISABLED,
|
||||||
|
description: "Disable parinfer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: ParinferMode.SMART,
|
||||||
|
description:
|
||||||
|
"Parinfer will try it's best to deduce when to run Paren mode and when to run Indent mode",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: ParinferMode.PAREN,
|
||||||
|
description:
|
||||||
|
"Parinfer will handle the indentation, you handle the parens",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: ParinferMode.INDENT,
|
||||||
|
description:
|
||||||
|
"Parinfer will handle the parens, you handle the indentation",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
void vscode.window
|
||||||
|
.showQuickPick(items, { title: "OpenGOAL Change Parinfer Mode" })
|
||||||
|
.then((v) => {
|
||||||
|
if (v !== undefined) {
|
||||||
|
changeParinferMode(v.label as ParinferMode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerParinferCommands(
|
||||||
|
context: vscode.ExtensionContext
|
||||||
|
): void {
|
||||||
|
changeParinferMode(
|
||||||
|
(getConfig().opengoalParinferMode as ParinferMode) ?? ParinferMode.DISABLED
|
||||||
|
);
|
||||||
|
updateStatus();
|
||||||
|
parinferStatusItem.show();
|
||||||
|
context.subscriptions.push(
|
||||||
|
vscode.commands.registerCommand(
|
||||||
|
"opengoal.parinfer.changeMode",
|
||||||
|
showChangeModeMenu
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
7
src/utils/editor-utils.ts
Normal file
7
src/utils/editor-utils.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import * as vscode from "vscode";
|
||||||
|
|
||||||
|
export function getEditorRange(editor: vscode.TextEditor) {
|
||||||
|
const document = editor.document;
|
||||||
|
const invalidRange = new vscode.Range(0, 0, document.lineCount + 5, 0);
|
||||||
|
return document.validateRange(invalidRange);
|
||||||
|
}
|
Loading…
Reference in a new issue