45 |
49 | ev
50 | |> ReactEvent.Mouse.clientY
51 | |> calculateBodyHeight(handleRef)
52 | |> Option.forEach(onResizeStart)
53 | }
54 | onDragEnd={ev =>
55 | ev
56 | |> ReactEvent.Mouse.clientY
57 | |> calculateBodyHeight(handleRef)
58 | |> Option.forEach(onResizeEnd)
59 | }
60 | /* to enable Drag & Drop */
61 | draggable=true
62 | tabIndex=(-1)
63 | />
64 |
65 | : null;
66 | };
67 |
--------------------------------------------------------------------------------
/lib/js/src/View/Emacs/Emacs__SearchAbout.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var React = require("react");
5 | var Rebase = require("@glennsl/rebase/lib/js/src/Rebase.bs.js");
6 | var Emacs__Parser$AgdaMode = require("./Emacs__Parser.bs.js");
7 | var Emacs__Component$AgdaMode = require("./Emacs__Component.bs.js");
8 |
9 | function parse(raw) {
10 | var lines = raw.split("\n");
11 | var target = Rebase.$$Option.getOr("???", Rebase.$$Option.map((function (param) {
12 | return param.slice(18);
13 | }), Rebase.$$Array.get(lines, 0)));
14 | var outputs = Rebase.$$Array.filterMap((function (x) {
15 | return x;
16 | }), Rebase.$$Array.map(Emacs__Component$AgdaMode.Output.parse, Emacs__Parser$AgdaMode.unindent(Rebase.$$Array.map((function (s) {
17 | return s.slice(2);
18 | }), lines.slice(1)))));
19 | return /* tuple */[
20 | target,
21 | outputs
22 | ];
23 | }
24 |
25 | function Emacs__SearchAbout(Props) {
26 | var body = Props.body;
27 | var match = parse(body);
28 | var outputs = match[1];
29 | var target = match[0];
30 | if (Rebase.$$Array.length(outputs) === 0) {
31 | return React.createElement("p", undefined, "There are no definitions about " + target);
32 | } else {
33 | return React.createElement(React.Fragment, undefined, React.createElement("p", undefined, "Definitions about " + (target + ":")), React.createElement("ul", undefined, Rebase.$$Array.mapi((function (value, i) {
34 | return React.createElement(Emacs__Component$AgdaMode.Output.make, {
35 | value: value,
36 | key: String(i)
37 | });
38 | }), outputs)));
39 | }
40 | }
41 |
42 | var make = Emacs__SearchAbout;
43 |
44 | exports.parse = parse;
45 | exports.make = make;
46 | /* react Not a pure module */
47 |
--------------------------------------------------------------------------------
/src/RunningInfo.re:
--------------------------------------------------------------------------------
1 | open Atom;
2 |
3 | type t = {
4 | mutable editor: option(TextEditor.t),
5 | mutable isOpeningEditor: bool,
6 | mutable buffer: array(string),
7 | mutable subscriptions: CompositeDisposable.t,
8 | };
9 | let make = () => {
10 | editor: None,
11 | isOpeningEditor: false,
12 | buffer: [||],
13 | subscriptions: CompositeDisposable.make(),
14 | };
15 |
16 | let itemOptions = {
17 | "initialLine": 0,
18 | "initialColumn": 0,
19 | "split": "right",
20 | "activatePane": false,
21 | "activateItem": false,
22 | "pending": false,
23 | "searchAllPanes": true,
24 | "location": (None: option(string)),
25 | };
26 |
27 | let destroy = self => {
28 | self.editor = None;
29 | self.isOpeningEditor = false;
30 | self.buffer = [||];
31 | self.subscriptions |> CompositeDisposable.dispose;
32 | };
33 |
34 | let add = (info, self) =>
35 | switch (self.editor) {
36 | | Some(editor) =>
37 | editor |> TextEditor.insertText(info) |> ignore;
38 | Promise.resolved();
39 | | None =>
40 | if (self.isOpeningEditor) {
41 | Js.Array.unshift(info, self.buffer) |> ignore;
42 | Promise.resolved();
43 | } else {
44 | self.isOpeningEditor = true;
45 | let itemURI = "agda-mode://running-info";
46 |
47 | Workspace.open_(itemURI, itemOptions)
48 | ->Promise.Js.fromBsPromise
49 | ->Promise.Js.toResult
50 | ->Promise.mapOk(newItem => {
51 | self.isOpeningEditor = false;
52 | // register the newly opened editor
53 | self.editor = Some(newItem);
54 | // insert logs in buffer to the editor and clean the buffer
55 | TextEditor.insertText(
56 | Js.String.concatMany(self.buffer, ""),
57 | newItem,
58 | )
59 | |> ignore;
60 | // empty the buffer
61 | self.buffer = [||];
62 | // destroy everything on close
63 |
64 | TextEditor.onDidDestroy(() => self |> destroy, newItem)
65 | |> CompositeDisposable.add(self.subscriptions);
66 | })
67 | ->Promise.map(_ => ());
68 | }
69 | };
--------------------------------------------------------------------------------
/src/Task/Task__DisplayInfo.re:
--------------------------------------------------------------------------------
1 | open Task;
2 | open! Type.View.Header;
3 |
4 | // Response.Info => Task
5 | let handle = (info: Response.Info.t): list(Task.t) =>
6 | switch (info) {
7 | | CompilationOk => [Display("Compilation Done!", Success, Nothing)]
8 | | Constraints(None) => [Display("No Constraints", Success, Nothing)]
9 | | Constraints(Some(payload)) => [
10 | Display("Constraints", Info, Emacs(Constraints(payload))),
11 | ]
12 | | AllGoalsWarnings(payload) => [
13 | Display(payload.title, Info, Emacs(AllGoalsWarnings(payload))),
14 | ]
15 | | Time(payload) => [
16 | Display("Time", PlainText, Emacs(PlainText(payload))),
17 | ]
18 | | Error(payload) => [Display("Error", Error, Emacs(Error(payload)))]
19 | | Intro(payload) => [
20 | Display("Intro", PlainText, Emacs(PlainText(payload))),
21 | ]
22 | | Auto(payload) => [
23 | Display("Auto", PlainText, Emacs(PlainText(payload))),
24 | ]
25 | | ModuleContents(payload) => [
26 | Display("Module Contents", Info, Emacs(PlainText(payload))),
27 | ]
28 | | SearchAbout(payload) => [
29 | Display(
30 | "Searching about ...",
31 | PlainText,
32 | Emacs(SearchAbout(payload)),
33 | ),
34 | ]
35 | | WhyInScope(payload) => [
36 | Display("Scope info", Info, Emacs(WhyInScope(payload))),
37 | ]
38 | | NormalForm(payload) => [
39 | Display("Normal form", Info, Emacs(PlainText(payload))),
40 | ]
41 | | GoalType(payload) => [
42 | Display("Goal type", Info, Emacs(GoalTypeContext(payload))),
43 | ]
44 | | CurrentGoal(payload) => [
45 | Display("Current goal", Info, Emacs(PlainText(payload))),
46 | ]
47 | | InferredType(payload) => [
48 | Display("Inferred type", Info, Emacs(PlainText(payload))),
49 | ]
50 | | Context(payload) => [
51 | Display("Context", Info, Emacs(Context(payload))),
52 | ]
53 | | HelperFunction(payload) => [
54 | Display("Helper function", Info, Emacs(PlainText(payload))),
55 | ]
56 | | Version(payload) => [
57 | Display("Version", Info, Emacs(PlainText(payload))),
58 | ]
59 | };
60 |
--------------------------------------------------------------------------------
/src/Type/Type__View.re:
--------------------------------------------------------------------------------
1 | open Type__Location;
2 |
3 | /* open Type__Type__Syntax.Name;
4 | open Type__Type__Syntax.Position;
5 | open Type__Type__Syntax.Concrete; */
6 |
7 | module Header = {
8 | type style =
9 | | PlainText
10 | | Error
11 | | Info
12 | | Success
13 | | Warning;
14 | type t = {
15 | text: string,
16 | style,
17 | };
18 | };
19 | /* action */
20 | type mountingTarget =
21 | | AtBottom
22 | | AtPane;
23 | /* state */
24 | type mountingPoint =
25 | | Bottom(Webapi.Dom.Element.t)
26 | | Pane(Tab.t);
27 | type mode =
28 | | Display
29 | | Inquire;
30 |
31 | module Mouse = {
32 | type event =
33 | | JumpToTarget(Range.linkTarget)
34 | | MouseOver(Range.linkTarget)
35 | | MouseOut(Range.linkTarget);
36 |
37 | let emitter = React.createContext((_ev: event) => ());
38 |
39 | module Provider = {
40 | [@bs.obj]
41 | external makeProps:
42 | (~value: event => unit, ~children: React.element, unit) =>
43 | {
44 | .
45 | "children": React.element,
46 | "value": event => unit,
47 | };
48 |
49 | let make = React.Context.provider(emitter);
50 | };
51 | };
52 |
53 | module Debug = {
54 | type inputMethod = {
55 | activated: bool,
56 | markers: array(Atom.DisplayMarker.t),
57 | buffer: Buffer.t,
58 | };
59 |
60 | type action =
61 | | UpdateInputMethod(inputMethod);
62 |
63 | type state = {inputMethod};
64 |
65 | let reducer = (_state, action) => {
66 | switch (action) {
67 | | UpdateInputMethod(inputMethod) => {inputMethod: inputMethod}
68 | };
69 | };
70 |
71 | let initialState = {
72 | inputMethod: {
73 | activated: false,
74 | markers: [||],
75 | buffer: Buffer.initial,
76 | },
77 | };
78 |
79 | let debugDispatch = React.createContext((_: action) => ());
80 |
81 | module Provider = {
82 | [@bs.obj]
83 | external makeProps:
84 | (~value: action => unit, ~children: React.element, unit) =>
85 | {
86 | .
87 | "children": React.element,
88 | "value": action => unit,
89 | };
90 | let make = React.Context.provider(debugDispatch);
91 | };
92 | };
--------------------------------------------------------------------------------
/lib/js/src/View/Panel/SizingHandle.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var React = require("react");
5 | var Rebase = require("@glennsl/rebase/lib/js/src/Rebase.bs.js");
6 | var Caml_option = require("bs-platform/lib/js/caml_option.js");
7 |
8 | function calculateBodyHeight(handleRef, handleY) {
9 | return Rebase.$$Option.flatMap((function (elem) {
10 | var top = (elem.getBoundingClientRect().top | 0) + 51 | 0;
11 | return Rebase.$$Option.flatMap((function (element) {
12 | var bottom = element.getBoundingClientRect().top | 0;
13 | if (top > 0) {
14 | return (bottom - handleY | 0) - 51 | 0;
15 | }
16 |
17 | }), Caml_option.nullable_to_opt(document.querySelector("atom-panel-container.footer")));
18 | }), Caml_option.nullable_to_opt(handleRef.current));
19 | }
20 |
21 | function SizingHandle(Props) {
22 | var onResizeStart = Props.onResizeStart;
23 | var onResizeEnd = Props.onResizeEnd;
24 | var mountAtBottom = Props.mountAtBottom;
25 | var handleRef = React.useRef(null);
26 | if (mountAtBottom) {
27 | return React.createElement("div", {
28 | className: "sizing-handle-anchor"
29 | }, React.createElement("div", {
30 | ref: handleRef,
31 | className: "sizing-handle native-key-bindings",
32 | draggable: true,
33 | tabIndex: -1,
34 | onDragEnd: (function (ev) {
35 | return Rebase.$$Option.forEach(onResizeEnd, calculateBodyHeight(handleRef, ev.clientY));
36 | }),
37 | onDragStart: (function (ev) {
38 | return Rebase.$$Option.forEach(onResizeStart, calculateBodyHeight(handleRef, ev.clientY));
39 | })
40 | }));
41 | } else {
42 | return null;
43 | }
44 | }
45 |
46 | var make = SizingHandle;
47 |
48 | exports.calculateBodyHeight = calculateBodyHeight;
49 | exports.make = make;
50 | /* react Not a pure module */
51 |
--------------------------------------------------------------------------------
/test/Test__Distribution.re:
--------------------------------------------------------------------------------
1 | open Rebase;
2 | open Fn;
3 |
4 | open! BsMocha.Mocha;
5 | open! BsMocha.Promise;
6 | open Js.Promise;
7 | open Test__Util;
8 |
9 | exception CannotReadPackageJson;
10 |
11 | // bindings for git-branch
12 | [@bs.module]
13 | external branch: unit => Js.Promise.t(Js.null(string)) = "git-branch";
14 |
15 | let readFile = N.Fs.readFile |> N.Util.promisify;
16 | let readPackageJSONMain = () => {
17 | readFile(. "./package.json")
18 | |> then_(
19 | Node.Buffer.toString
20 | >> Js.Json.parseExn
21 | >> Js.Json.decodeObject
22 | >> Option.flatMap(obj => Js.Dict.get(obj, "main"))
23 | >> Option.flatMap(Js.Json.decodeString)
24 | >> Option.mapOr(resolve, reject(CannotReadPackageJson)),
25 | );
26 | };
27 |
28 | let onProd = callback =>
29 | branch()
30 | |> then_(x =>
31 | switch (Js.nullToOption(x)) {
32 | | Some("master") => callback()
33 | | _ => resolve(0)
34 | }
35 | );
36 |
37 | let onDev = callback =>
38 | branch()
39 | |> then_(x =>
40 | switch (Js.nullToOption(x)) {
41 | | Some("master") => resolve(0)
42 | | _ => callback()
43 | }
44 | );
45 |
46 | describe("Distribution", () => {
47 | describe("when on the master branch", () => {
48 | it("has the production bundle ready", () =>
49 | onProd(() =>
50 | make((~resolve, ~reject) =>
51 | N.Fs.access(Path.file("lib/js/bundled.js"), err =>
52 | switch (Js.nullToOption(err)) {
53 | | None => resolve(. 0)
54 | | Some(e) => reject(. N.Exception(e))
55 | }
56 | )
57 | )
58 | )
59 | );
60 |
61 | it("should points to the production bundle", () =>
62 | onProd(() =>
63 | readPackageJSONMain()
64 | |> then_(path => {
65 | path |> Assert.equal("./lib/js/bundled.js");
66 | resolve(0);
67 | })
68 | )
69 | );
70 | });
71 |
72 | describe("when on the development branch", () =>
73 | it("should points to AgdaMode.bs", () =>
74 | onDev(() =>
75 | readPackageJSONMain()
76 | |> then_(path => {
77 | path |> Assert.equal("./lib/js/src/AgdaMode.bs");
78 | resolve(0);
79 | })
80 | )
81 | )
82 | );
83 | });
84 |
--------------------------------------------------------------------------------
/src/Instance.re:
--------------------------------------------------------------------------------
1 | module Event = Event;
2 |
3 | open Instance__Type;
4 | module Goals = Instance__Goals;
5 | module Highlightings = Instance__Highlightings;
6 | module Connections = Instance__Connections;
7 | module TextEditors = Instance__TextEditors;
8 |
9 | type t = Instance__Type.t;
10 |
11 | let activate = instance =>
12 | // only activate the view when it's loaded
13 | if (instance.isLoaded) {
14 | instance.view.activate();
15 | } else {
16 | Promise.resolved();
17 | };
18 |
19 | let deactivate = instance =>
20 | // only deactivate the view when it's loaded
21 | if (instance.isLoaded) {
22 | instance.view.deactivate();
23 | } else {
24 | Promise.resolved();
25 | };
26 |
27 | let destroy = instance => instance.view.destroy();
28 |
29 | let make = (textEditor: Atom.TextEditor.t) => {
30 | /* adds "agda" to the class-list */
31 | Atom.Views.getView(textEditor)
32 | |> Webapi.Dom.HtmlElement.classList
33 | |> Webapi.Dom.DomTokenList.add("agda");
34 |
35 | /* */
36 | let editors = Editors.make(textEditor);
37 | let view = Root.initialize(editors);
38 | let instance = {
39 | isLoaded: false,
40 | editors,
41 | view,
42 | goals: [||],
43 | history: {
44 | checkpoints: [||],
45 | needsReloading: false,
46 | },
47 | highlightings: [||],
48 | runningInfo: RunningInfo.make(),
49 | connection: None,
50 | onDispatch: Event.make(),
51 | onConnectionError: Event.make(),
52 | };
53 |
54 | // subscribe to `onMouseEvent`
55 | let destructor1 =
56 | instance.view.onMouseEvent.on(ev =>
57 | switch (ev) {
58 | | Type.View.Mouse.JumpToTarget(target) =>
59 | TaskRunner.dispatchCommand(Jump(target), instance) |> ignore
60 | | _ => ()
61 | }
62 | );
63 | // unsubscribe to `onMouseEvent`
64 | instance.view.onDestroy.once()->Promise.get(_ => destructor1());
65 |
66 | instance;
67 | };
68 |
69 | let dispatchUndo = (instance: t) => {
70 | // should reset goals after undo
71 | instance.editors.source |> Atom.TextEditor.undo;
72 | // reload
73 | if (instance.history.needsReloading) {
74 | TaskRunner.dispatchCommand(Load, instance) |> ignore;
75 | instance.history.needsReloading = false;
76 | };
77 | };
78 |
79 | let getID = (instance: t): string => {
80 | instance.editors |> Editors.getID;
81 | };
82 |
--------------------------------------------------------------------------------
/lib/js/src/RunningInfo.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Atom = require("atom");
5 | var $$Promise = require("reason-promise/lib/js/src/js/promise.js");
6 | var Caml_option = require("bs-platform/lib/js/caml_option.js");
7 | var Caml_splice_call = require("bs-platform/lib/js/caml_splice_call.js");
8 |
9 | function make(param) {
10 | return {
11 | editor: undefined,
12 | isOpeningEditor: false,
13 | buffer: [],
14 | subscriptions: new Atom.CompositeDisposable()
15 | };
16 | }
17 |
18 | var itemOptions = {
19 | initialLine: 0,
20 | initialColumn: 0,
21 | split: "right",
22 | activatePane: false,
23 | activateItem: false,
24 | pending: false,
25 | searchAllPanes: true,
26 | location: undefined
27 | };
28 |
29 | function destroy(self) {
30 | self.editor = undefined;
31 | self.isOpeningEditor = false;
32 | self.buffer = [];
33 | self.subscriptions.dispose();
34 | return /* () */0;
35 | }
36 |
37 | function add(info, self) {
38 | var match = self.editor;
39 | if (match !== undefined) {
40 | Caml_option.valFromOption(match).insertText(info);
41 | return $$Promise.resolved(/* () */0);
42 | } else if (self.isOpeningEditor) {
43 | self.buffer.unshift(info);
44 | return $$Promise.resolved(/* () */0);
45 | } else {
46 | self.isOpeningEditor = true;
47 | return $$Promise.map($$Promise.mapOk($$Promise.Js.toResult($$Promise.Js.fromBsPromise(atom.workspace.open("agda-mode://running-info", itemOptions))), (function (newItem) {
48 | self.isOpeningEditor = false;
49 | self.editor = Caml_option.some(newItem);
50 | newItem.insertText(Caml_splice_call.spliceObjApply("", "concat", [self.buffer]));
51 | self.buffer = [];
52 | self.subscriptions.add(newItem.onDidDestroy((function (param) {
53 | return destroy(self);
54 | })));
55 | return /* () */0;
56 | })), (function (param) {
57 | return /* () */0;
58 | }));
59 | }
60 | }
61 |
62 | exports.make = make;
63 | exports.itemOptions = itemOptions;
64 | exports.destroy = destroy;
65 | exports.add = add;
66 | /* atom Not a pure module */
67 |
--------------------------------------------------------------------------------
/lib/js/src/View/Emacs/Emacs__Body.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var React = require("react");
5 | var Emacs__Error$AgdaMode = require("./Emacs__Error.bs.js");
6 | var Emacs__Context$AgdaMode = require("./Emacs__Context.bs.js");
7 | var Emacs__ParseError$AgdaMode = require("./Emacs__ParseError.bs.js");
8 | var Emacs__WhyInScope$AgdaMode = require("./Emacs__WhyInScope.bs.js");
9 | var Emacs__SearchAbout$AgdaMode = require("./Emacs__SearchAbout.bs.js");
10 | var Emacs__GoalTypeContext$AgdaMode = require("./Emacs__GoalTypeContext.bs.js");
11 | var Emacs__AllGoalsWarnings$AgdaMode = require("./Emacs__AllGoalsWarnings.bs.js");
12 |
13 | function Emacs__Body(Props) {
14 | var data = Props.data;
15 | switch (data.tag | 0) {
16 | case /* AllGoalsWarnings */0 :
17 | return React.createElement(Emacs__AllGoalsWarnings$AgdaMode.make, {
18 | value: data[0]
19 | });
20 | case /* GoalTypeContext */1 :
21 | return React.createElement(Emacs__GoalTypeContext$AgdaMode.make, {
22 | body: data[0]
23 | });
24 | case /* Context */2 :
25 | case /* Constraints */3 :
26 | break;
27 | case /* WhyInScope */4 :
28 | return React.createElement(Emacs__WhyInScope$AgdaMode.make, {
29 | body: data[0]
30 | });
31 | case /* SearchAbout */5 :
32 | return React.createElement(Emacs__SearchAbout$AgdaMode.make, {
33 | body: data[0]
34 | });
35 | case /* Error */6 :
36 | return React.createElement(Emacs__Error$AgdaMode.make, {
37 | body: data[0]
38 | });
39 | case /* ParseError */7 :
40 | return React.createElement(Emacs__ParseError$AgdaMode.make, {
41 | connection: data[0]
42 | });
43 | case /* PlainText */8 :
44 | var body = data[0];
45 | if (body === "") {
46 | return null;
47 | } else {
48 | return React.createElement("p", undefined, body);
49 | }
50 |
51 | }
52 | return React.createElement(Emacs__Context$AgdaMode.make, {
53 | body: data[0]
54 | });
55 | }
56 |
57 | var make = Emacs__Body;
58 |
59 | exports.make = make;
60 | /* react Not a pure module */
61 |
--------------------------------------------------------------------------------
/src/View/Hook.re:
--------------------------------------------------------------------------------
1 | open Rebase;
2 |
3 | let useDidUpdateEffect = (f, inputs) => {
4 | let didMountRef = React.useRef(false);
5 | React.useEffect1(
6 | () =>
7 | if (React.Ref.current(didMountRef)) {
8 | f();
9 | } else {
10 | React.Ref.setCurrent(didMountRef, true);
11 | None;
12 | },
13 | inputs,
14 | );
15 | };
16 |
17 | let useDidUpdateEffect2 = (f, (a, b)) => {
18 | let didMountRef = React.useRef(false);
19 | React.useEffect2(
20 | () =>
21 | if (React.Ref.current(didMountRef)) {
22 | f();
23 | } else {
24 | React.Ref.setCurrent(didMountRef, true);
25 | None;
26 | },
27 | (a, b),
28 | );
29 | };
30 |
31 | let useState = init => {
32 | let (state, setState) = React.useState(_ => init);
33 | let setState' = value => setState(_ => value);
34 | (state, setState');
35 | };
36 |
37 | let useAtomListener = listener => {
38 | React.useEffect(() => {
39 | let destructor = listener();
40 | Some(() => Atom.Disposable.dispose(destructor));
41 | });
42 | };
43 |
44 | let useAtomListenerWhen = (listener, shouldListen) => {
45 | let (destructor, setDestructor) = React.useState(_ => None);
46 |
47 | React.useEffect1(
48 | () => {
49 | if (shouldListen) {
50 | let destructor = listener();
51 | setDestructor(_ => Some(() => Atom.Disposable.dispose(destructor)));
52 | } else {
53 | // execute the destructor
54 | destructor |> Option.forEach(f => f());
55 | };
56 |
57 | // return the destructor in case that it got unmounted
58 | destructor;
59 | },
60 | [|shouldListen|],
61 | );
62 | };
63 |
64 | let useListenWhen = (listener, shouldListen) => {
65 | let (destructor, setDestructor) = React.useState(_ => None);
66 |
67 | React.useEffect1(
68 | () => {
69 | if (shouldListen) {
70 | setDestructor(_ => listener());
71 | } else {
72 | // execute the destructor
73 | destructor
74 | |> Option.forEach(f => {
75 | f();
76 | setDestructor(_ => None);
77 | });
78 | };
79 | None;
80 | },
81 | // destructor;
82 | [|shouldListen|],
83 | );
84 | };
85 |
86 | let useChannel = (callback, channel) => {
87 | React.useEffect1(
88 | () => {
89 | channel |> Channel.recv(callback);
90 | None;
91 | },
92 | [||],
93 | );
94 | };
95 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | strategy:
11 | matrix:
12 | node-version: [10.x]
13 |
14 | steps:
15 | - uses: actions/checkout@v1
16 | - name: Setup Node.js
17 | uses: actions/setup-node@v1
18 | with:
19 | node-version: ${{ matrix.node-version }}
20 |
21 | - name: Install atom
22 | run: |
23 | curl -s -L "https://atom.io/download/deb?channel=stable" -H 'Accept: application/octet-stream' -o "atom-amd64.deb"
24 | /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16
25 | export DISPLAY=":99"
26 | dpkg-deb -x atom-amd64.deb "${HOME}/atom"
27 | export ATOM_SCRIPT_NAME="atom"
28 | export APM_SCRIPT_NAME="apm"
29 | export ATOM_SCRIPT_PATH="${HOME}/atom/usr/bin/${ATOM_SCRIPT_NAME}"
30 | export APM_SCRIPT_PATH="${HOME}/atom/usr/bin/${APM_SCRIPT_NAME}"
31 | export NPM_SCRIPT_PATH="${HOME}/atom/usr/share/${ATOM_SCRIPT_NAME}/resources/app/apm/node_modules/.bin/npm"
32 | export PATH="${PATH}:${HOME}/atom/usr/bin"
33 |
34 | echo "Using Atom version:"
35 | "${ATOM_SCRIPT_PATH}" -v
36 | echo "Using APM version:"
37 | "${APM_SCRIPT_PATH}" -v
38 |
39 | - name: Install Agda
40 | run: |
41 | echo "Pulling Agda from Docker Hub"
42 | docker pull banacorn/agda:2.6.0.1
43 |
44 | echo "Create a script for aliasing docker run"
45 | mkdir ${HOME}/bin
46 | export PATH="${PATH}:${HOME}/bin"
47 | echo -e '#!/bin/bash\ndocker run -i --rm banacorn/agda:2.6.0.1 agda "$@"' > ${HOME}/bin/agda
48 | chmod +x ${HOME}/bin/agda
49 |
50 | - name: npm install & build
51 | run: |
52 | npm install
53 | npm run build --if-present
54 |
55 | - name: Run tests
56 | run: |
57 |
58 | # so that we can see "atom" and "apm" and "agda"
59 | export PATH="${PATH}:${HOME}/atom/usr/bin:${HOME}/bin"
60 |
61 | echo "Version of Agda: "
62 | agda -V
63 | echo "Path of Agda: "
64 | which agda
65 |
66 | # linking "agda-mode" to "~/.atom/dev/packages"
67 | apm dev agda-mode
68 |
69 | # run the test with "xvfb-run" to simulate X windows
70 | xvfb-run -a atom -t lib/js/test/
71 | env:
72 | CI: true
73 |
--------------------------------------------------------------------------------
/lib/js/src/View/Settings/Settings__Breadcrumb.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Curry = require("bs-platform/lib/js/curry.js");
5 | var React = require("react");
6 |
7 | function Settings__Breadcrumb(Props) {
8 | var uri = Props.uri;
9 | var onNavigate = Props.onNavigate;
10 | var tmp;
11 | switch (uri) {
12 | case /* Root */0 :
13 | tmp = null;
14 | break;
15 | case /* Connection */1 :
16 | tmp = React.createElement("li", undefined, React.createElement("a", {
17 | href: "#"
18 | }, React.createElement("span", {
19 | className: "icon icon-plug"
20 | }, "Connection")));
21 | break;
22 | case /* Log */2 :
23 | tmp = React.createElement("li", undefined, React.createElement("a", {
24 | href: "#"
25 | }, React.createElement("span", {
26 | className: "icon icon-comment-discussion"
27 | }, "Log")));
28 | break;
29 | case /* InputMethod */3 :
30 | tmp = React.createElement("li", undefined, React.createElement("a", {
31 | href: "#"
32 | }, React.createElement("span", {
33 | className: "icon icon-keyboard"
34 | }, "Input Method")));
35 | break;
36 | case /* Debug */4 :
37 | tmp = React.createElement("li", undefined, React.createElement("a", {
38 | href: "#"
39 | }, React.createElement("span", {
40 | className: "icon icon-terminal"
41 | }, "Debug")));
42 | break;
43 |
44 | }
45 | return React.createElement("nav", {
46 | className: "agda-settings-breadcrumb"
47 | }, React.createElement("ol", {
48 | className: "breadcrumb"
49 | }, React.createElement("li", undefined, React.createElement("a", {
50 | href: "#",
51 | onClick: (function (param) {
52 | return Curry._1(onNavigate, /* Root */0);
53 | })
54 | }, React.createElement("span", {
55 | className: "icon icon-settings"
56 | }, "Settings"))), tmp));
57 | }
58 |
59 | var make = Settings__Breadcrumb;
60 |
61 | exports.make = make;
62 | /* react Not a pure module */
63 |
--------------------------------------------------------------------------------
/src/View/Channels.re:
--------------------------------------------------------------------------------
1 | open Type.View;
2 |
3 | module Event = Event;
4 | // open Event;
5 |
6 | type t = {
7 | // lifecycle
8 | destroy: Channel.t(unit, unit),
9 | //
10 | activatePanel: Channel.t(unit, unit),
11 | deactivatePanel: Channel.t(unit, unit),
12 | toggleDocking: Channel.t(unit, unit),
13 | display: Channel.t((Header.t, Body.t), unit),
14 | inquire:
15 | Channel.t(
16 | (Header.t, string, string),
17 | Rebase.result(string, MiniEditor.error),
18 | ),
19 | updateIsPending: Channel.t(bool, unit),
20 | // updateShouldDisplay: Channel.t(bool, unit, unit),
21 | // Input Method
22 | /*
23 | Issue #34: https://github.com/banacorn/agda-mode/issues/34
24 | Intercept some keys that Bracket Matcher autocompletes
25 | to name them all: "{", "[", "{", "\"", "'", and `
26 | Because the Bracket Matcher package is too lacking, it does not responds
27 | to the disabling of the package itself, making it impossible to disable
28 | the package during the process of input.
29 | Instead, we hardwire the keys we wanna intercept directly from the Keymaps.
30 | */
31 | activateInputMethod: Channel.t(bool, unit),
32 | interceptAndInsertKey: Channel.t(string, unit),
33 | //
34 | navigateSettings: Channel.t(option(Settings__Breadcrumb.uri), unit),
35 | updateConnection:
36 | Channel.t((option(Connection.t), option(Connection.Error.t)), unit),
37 | inquireConnection:
38 | Channel.t(unit, Rebase.result(string, MiniEditor.error)),
39 | };
40 |
41 | /* creates all refs and return them */
42 | let make = () => {
43 | // lifecycle
44 | destroy: Channel.make(),
45 | //
46 | activatePanel: Channel.make(),
47 | deactivatePanel: Channel.make(),
48 | toggleDocking: Channel.make(),
49 |
50 | display: Channel.make(),
51 | inquire: Channel.make(),
52 |
53 | updateIsPending: Channel.make(),
54 |
55 | //
56 | activateInputMethod: Channel.make(),
57 | interceptAndInsertKey: Channel.make(),
58 |
59 | //
60 | navigateSettings: Channel.make(),
61 | updateConnection: Channel.make(),
62 | inquireConnection: Channel.make(),
63 | };
64 |
65 | let context = React.createContext(make());
66 |
67 | module Provider = {
68 | [@bs.obj]
69 | external makeProps:
70 | (~value: t, ~children: React.element, unit) =>
71 | {
72 | .
73 | "children": React.element,
74 | "value": t,
75 | } =
76 | "";
77 | let make = React.Context.provider(context);
78 | };
79 |
--------------------------------------------------------------------------------
/lib/js/test/Parser/Test__Parser__Response.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Curry = require("bs-platform/lib/js/curry.js");
5 | var Belt_Array = require("bs-platform/lib/js/belt_Array.js");
6 | var Mocha$BsMocha = require("bs-mocha/lib/js/src/Mocha.bs.js");
7 | var Promise$BsMocha = require("bs-mocha/lib/js/src/Promise.bs.js");
8 | var Response$AgdaMode = require("../../src/Response.bs.js");
9 | var Test__Util$AgdaMode = require("../Test__Util.bs.js");
10 | var Test__Parser__SExpression$AgdaMode = require("./Test__Parser__SExpression.bs.js");
11 |
12 | function toResponses(exprs) {
13 | return Belt_Array.concatMany(Belt_Array.map(Belt_Array.map(exprs, Response$AgdaMode.parse), (function (param) {
14 | if (param.tag) {
15 | Curry._1(Test__Util$AgdaMode.Assert.fail, param[0]);
16 | return [];
17 | } else {
18 | return [param[0]];
19 | }
20 | })));
21 | }
22 |
23 | Mocha$BsMocha.describe("when parsing responses")(undefined, undefined, undefined, (function (param) {
24 | return Belt_Array.forEach(Test__Util$AgdaMode.Golden.getGoldenFilepathsSync("test/Parser/Response"), (function (filepath) {
25 | return Promise$BsMocha.it("should golden test " + filepath)(undefined, undefined, undefined, (function (param) {
26 | return Test__Util$AgdaMode.Golden.readFile(filepath).then((function (raw) {
27 | var partial_arg = [];
28 | return Test__Util$AgdaMode.Golden.compare(Test__Util$AgdaMode.Golden.map(Test__Util$AgdaMode.Golden.map(Test__Util$AgdaMode.Golden.map(raw, (function (param) {
29 | return Test__Parser__SExpression$AgdaMode.parseSExpression(partial_arg, param);
30 | })), toResponses), (function (param) {
31 | return Test__Util$AgdaMode.serializeWith(Response$AgdaMode.toString, param);
32 | })));
33 | }));
34 | }));
35 | }));
36 | }));
37 |
38 | exports.toResponses = toResponses;
39 | /* Not a pure module */
40 |
--------------------------------------------------------------------------------
/src/Editors.re:
--------------------------------------------------------------------------------
1 | open Belt;
2 |
3 | type sort =
4 | | Source
5 | | Query;
6 |
7 | type t = {
8 | mutable focused: sort,
9 | source: Atom.TextEditor.t,
10 | mutable query: option(Atom.TextEditor.t),
11 | };
12 |
13 | let make = editor => {focused: Source, source: editor, query: None};
14 |
15 | let getID = self => string_of_int(Atom.TextEditor.id(self.source));
16 |
17 | module Focus = {
18 | open Atom;
19 | open Webapi.Dom;
20 | let get = (editors): TextEditor.t =>
21 | switch (editors.focused) {
22 | | Source => editors.source
23 | | Query =>
24 | switch (editors.query) {
25 | | Some(editor) => editor
26 | | None => editors.source
27 | }
28 | };
29 |
30 | let on = (sort, editors) =>
31 | switch (sort) {
32 | | Source =>
33 | editors.source->Views.getView->HtmlElement.focus;
34 | editors.focused = Source;
35 | | Query =>
36 | editors.query
37 | ->Option.map(Atom.Views.getView)
38 | ->Option.forEach(HtmlElement.focus);
39 | editors.focused = Query;
40 | };
41 | };
42 |
43 | module Selection = {
44 | let getSymbol = editors => {
45 | editors
46 | ->Focus.get
47 | ->Atom.TextEditor.getSelectedText
48 | ->Js.String.substrAtMost(~from=0, ~length=1);
49 | };
50 | let getTextNode = editors => {
51 | let getText = () => {
52 | editors |> Focus.get |> Atom.TextEditor.getSelectedText;
53 | };
54 | let getLargerSyntaxNode = () => {
55 | editors |> Focus.get |> Atom.TextEditor.selectLargerSyntaxNode |> ignore;
56 | editors |> Focus.get |> Atom.TextEditor.getSelectedText;
57 | };
58 | let getPointedWord = () => {
59 | editors
60 | |> Focus.get
61 | |> Atom.TextEditor.selectWordsContainingCursors
62 | |> ignore;
63 | editors |> Focus.get |> Atom.TextEditor.getSelectedText;
64 | };
65 |
66 | let selectedText = getText();
67 |
68 | // if the user didn't select anything
69 | if (selectedText == "") {
70 | let largerNode = getLargerSyntaxNode();
71 | // this happens when language-agda is not installed
72 | if (largerNode == "") {
73 | getPointedWord();
74 | } else {
75 | let pointedText = getPointedWord();
76 | // this happens when the user is hovering on a mixfix/infix operator like _+_
77 | if (pointedText == "_") {
78 | getLargerSyntaxNode();
79 | } else {
80 | pointedText;
81 | };
82 | };
83 | } else {
84 | selectedText;
85 | };
86 | };
87 | };
--------------------------------------------------------------------------------
/lib/js/test/Test__Misc.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var $$Promise = require("reason-promise/lib/js/src/js/promise.js");
5 | var Mocha$BsMocha = require("bs-mocha/lib/js/src/Mocha.bs.js");
6 | var Assert$BsMocha = require("bs-mocha/lib/js/src/Assert.bs.js");
7 | var Promise$BsMocha = require("bs-mocha/lib/js/src/Promise.bs.js");
8 | var Instance$AgdaMode = require("../src/Instance.bs.js");
9 | var Connection$AgdaMode = require("../src/Connection.bs.js");
10 | var TaskRunner$AgdaMode = require("../src/Task/TaskRunner.bs.js");
11 | var Test__Util$AgdaMode = require("./Test__Util.bs.js");
12 | var Instance__Connections$AgdaMode = require("../src/Instance/Instance__Connections.bs.js");
13 |
14 | Mocha$BsMocha.describe_skip("when loading files")(undefined, undefined, undefined, (function (param) {
15 | return Mocha$BsMocha.describe("when parsing responses from Agda")(undefined, undefined, undefined, (function (param) {
16 | return Promise$BsMocha.it("should succeed")(undefined, undefined, undefined, (function (param) {
17 | var path = Test__Util$AgdaMode.Path.asset("Algebra.agda");
18 | return Test__Util$AgdaMode.$$File.open_(path).then((function (editor) {
19 | var instance = Instance$AgdaMode.make(editor);
20 | return $$Promise.Js.toBsPromise($$Promise.flatMap($$Promise.mapError($$Promise.mapOk($$Promise.mapOk($$Promise.mapOk($$Promise.flatMapOk(Connection$AgdaMode.autoSearch("agda"), (function (path) {
21 | return Connection$AgdaMode.validateAndMake(path, [" --no-libraries"]);
22 | })), Connection$AgdaMode.connect), Connection$AgdaMode.wire), (function (param) {
23 | return Instance__Connections$AgdaMode.persistConnection(instance, param);
24 | })), Assert$BsMocha.fail), (function (param) {
25 | return TaskRunner$AgdaMode.dispatchCommand(/* Load */0, instance);
26 | })));
27 | }));
28 | }));
29 | }));
30 | }));
31 |
32 | /* Not a pure module */
33 |
--------------------------------------------------------------------------------
/lib/js/src/View/Settings/Settings__Debug.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var React = require("react");
5 | var Rebase = require("@glennsl/rebase/lib/js/src/Rebase.bs.js");
6 | var Util$AgdaMode = require("../../Util/Util.bs.js");
7 | var Buffer$AgdaMode = require("../Panel/InputMethod/Buffer.bs.js");
8 |
9 | function Settings__Debug(Props) {
10 | var debug = Props.debug;
11 | var hidden = Props.hidden;
12 | var inputMethod = debug.inputMethod;
13 | return React.createElement("section", {
14 | className: "agda-settings-debug" + Util$AgdaMode.React.showWhen(!hidden)
15 | }, React.createElement("h1", undefined, React.createElement("span", {
16 | className: "icon icon-terminal"
17 | }), React.createElement("span", undefined, "Debug")), React.createElement("p", undefined, "only available in dev mode"), React.createElement("hr", undefined), React.createElement("h2", undefined, React.createElement("span", undefined, "Input Method"), inputMethod.activated ? React.createElement("span", {
18 | className: "icon icon-primitive-dot text-success",
19 | id: "input-method-activation",
20 | title: "activated"
21 | }) : React.createElement("span", {
22 | className: "icon icon-primitive-dot text-error",
23 | id: "input-method-activation",
24 | title: "deactivated"
25 | })), React.createElement("p", undefined, "input sequence : ", React.createElement("span", {
26 | className: "inline-block highlight"
27 | }, Buffer$AgdaMode.toSequence(inputMethod.buffer))), React.createElement("p", undefined, "output buffer : ", React.createElement("span", {
28 | className: "inline-block highlight"
29 | }, Buffer$AgdaMode.toSurface(inputMethod.buffer))), React.createElement("p", undefined, "# of markers : ", React.createElement("span", {
30 | className: "inline-block highlight"
31 | }, String(Rebase.$$Array.length(inputMethod.markers)))), React.createElement("div", {
32 | className: "block"
33 | }, React.createElement("button", {
34 | className: "btn",
35 | onClick: (function (param) {
36 | console.log(inputMethod.markers);
37 | return /* () */0;
38 | })
39 | }, "Dump Markers")));
40 | }
41 |
42 | var make = Settings__Debug;
43 |
44 | exports.make = make;
45 | /* react Not a pure module */
46 |
--------------------------------------------------------------------------------
/src/View/Settings/Settings.re:
--------------------------------------------------------------------------------
1 | open ReasonReact;
2 | open Util.React;
3 | open Rebase.Fn;
4 |
5 | module URI = Settings__Breadcrumb;
6 | type uri = URI.uri;
7 |
8 | let at = (x, y, classNames) => {
9 | classNames ++ showWhen(x == y);
10 | };
11 |
12 | [@react.component]
13 | let make =
14 | // ~inquireConnection: Event.t(unit, unit),
15 | // ~onInquireConnection: Event.t(string, MiniEditor.error),
16 | (
17 | ~targetURI: uri,
18 | ~debug: Type.View.Debug.state,
19 | ~element: option(Webapi.Dom.Element.t),
20 | ) => {
21 | let channels = React.useContext(Channels.context);
22 | let ((connection, connectionError), setConnectionAndError) =
23 | Hook.useState((None, None));
24 | Hook.useChannel(
25 | setConnectionAndError >> Promise.resolved,
26 | channels.updateConnection,
27 | );
28 |
29 | let (uri, setURI) = Hook.useState(targetURI);
30 | React.useEffect1(
31 | () => {
32 | setURI(targetURI);
33 | None;
34 | },
35 | [|targetURI|],
36 | );
37 |
38 | let inDevMode = Atom.inDevMode();
39 | switch (element) {
40 | | None => null
41 | | Some(anchor) =>
42 | ReactDOMRe.createPortal(
43 |
44 |
45 |
46 |
47 | - setURI(URI.Connection)}>
48 | {string("Connection")}
49 |
50 | - setURI(URI.Log)}>
51 |
52 | {string("Log")}
53 |
54 |
55 | - setURI(URI.InputMethod)}>
56 |
57 | {string("Input Method")}
58 |
59 |
60 | {inDevMode
61 | ? - setURI(URI.Debug)}>
62 |
63 | {string("Debug")}
64 |
65 |
66 | : null}
67 |
68 |
75 |
76 |
77 |
78 |
79 | ,
80 | anchor,
81 | )
82 | };
83 | };
84 |
--------------------------------------------------------------------------------
/src/View/Panel/InputMethod/Translator.re:
--------------------------------------------------------------------------------
1 | open Rebase;
2 |
3 | // key - symbols mapping
4 |
5 | [@bs.module "./../../../../../../asset/query.js"]
6 | external rawTable: Js.Dict.t(array(string)) = "default";
7 |
8 | // trie
9 |
10 | type trie = {
11 | symbol: array(string),
12 | subTrie: Js.Dict.t(trie),
13 | };
14 | [@bs.module "./../../../../../../asset/keymap.js"]
15 | external rawKeymapObject: Js.t({.}) = "default";
16 |
17 | let rec fromObject = (obj: Js.t({.})): trie => {
18 | let symbol = [%raw {|
19 | obj[">>"] || []
20 | |}];
21 | let subTrie =
22 | obj
23 | |> Js.Obj.keys
24 | |> Array.filter(key => key != ">>")
25 | |> Array.map(key => (key, fromObject([%raw {|
26 | obj[key]
27 | |}])))
28 | |> Js.Dict.fromArray;
29 | {symbol, subTrie};
30 | };
31 |
32 | let keymap = fromObject(rawKeymapObject);
33 |
34 | let toKeySuggestions = (trie: trie): array(string) =>
35 | Js.Dict.keys(trie.subTrie);
36 |
37 | let toCandidateSymbols = (trie: trie): array(string) => trie.symbol;
38 |
39 | // see if the key sequence is in the keymap
40 | // returns (KeySuggestions, CandidateSymbols)
41 | let isInKeymap = (input: string): option(trie) => {
42 | let rec helper = (input: string, trie: trie): option(trie) =>
43 | switch (String.length(input)) {
44 | | 0 => Some(trie)
45 | | n =>
46 | let key = String.sub(~from=0, ~length=1, input);
47 | let rest = String.sub(~from=1, ~length=n - 1, input);
48 | switch (Js.Dict.get(trie.subTrie, key)) {
49 | | Some(trie') => helper(rest, trie')
50 | | None => None
51 | };
52 | };
53 | helper(input, keymap);
54 | };
55 |
56 | // // see if the key sequence is in the extension */
57 | // let isInExtension = (input: string): option(trie) => {};
58 |
59 | type translation = {
60 | symbol: option(string),
61 | further: bool,
62 | keySuggestions: array(string),
63 | candidateSymbols: array(string),
64 | };
65 |
66 | /* converts characters to symbol, and tells if there's any further possible combinations */
67 | let translate = (input: string): translation => {
68 | let trie = isInKeymap(input);
69 | let keySuggestions =
70 | trie
71 | |> Option.mapOr(toKeySuggestions, [||])
72 | |> Extension.extendKeySuggestions(input);
73 |
74 | let candidateSymbols =
75 | trie
76 | |> Option.mapOr(toCandidateSymbols, [||])
77 | |> Extension.extendCandidateSymbols(input);
78 |
79 | {
80 | symbol: candidateSymbols[0],
81 | further: Array.length(keySuggestions) != 0,
82 | keySuggestions,
83 | candidateSymbols,
84 | };
85 | };
86 | let initialTranslation = translate("");
87 |
88 | let lookup = (symbol): option(array(string)) => {
89 | symbol
90 | |> Js.String.codePointAt(0)
91 | |> Option.map(string_of_int)
92 | |> Option.flatMap(Js.Dict.get(rawTable));
93 | };
--------------------------------------------------------------------------------
/src/Log.re:
--------------------------------------------------------------------------------
1 | open Belt;
2 | module Entry = {
3 | type request = Request.t;
4 | type response = {
5 | mutable rawText: array(string),
6 | mutable sexpression: array(Parser.SExpression.t),
7 | mutable response: array(Response.t),
8 | mutable error: array(Parser.Error.t),
9 | };
10 | type t = {
11 | request,
12 | response,
13 | };
14 | let make = cmd => {
15 | request: cmd,
16 | response: {
17 | rawText: [||],
18 | sexpression: [||],
19 | response: [||],
20 | error: [||],
21 | },
22 | };
23 | let serialize = (i, self) => {
24 | // indent some paragraph by 4 spaces
25 | let indent = xs =>
26 | xs
27 | ->Js.String.splitByRe([%re "/\\n/"], _)
28 | ->Array.map(Option.mapWithDefault(_, "", x => " " ++ x))
29 | ->Js.String.concatMany("\n");
30 | let fold = (text, title) => {j| $title
31 |
32 |
33 | $text
34 |
35 |
36 |
37 | |j};
38 | let quote = (xs, title) =>
39 | xs
40 | ->Array.map(x => {j|```
41 | $x
42 | ```
43 | |j})
44 | ->Js.String.concatMany("\n")
45 | ->fold(title)
46 | ->indent;
47 |
48 | let request = Request.toString(self.request);
49 |
50 | let rawText = self.response.rawText->quote("raw text");
51 | let sexpression =
52 | self.response.sexpression
53 | ->Array.map(Parser.SExpression.toString)
54 | ->quote("s-expression");
55 | let response =
56 | self.response.response
57 | ->Array.map(Response.toString)
58 | ->quote("response");
59 | let error =
60 | self.response.error->Array.map(Parser.Error.toString)->quote("error");
61 |
62 | {j|$i. **$request**
63 | $rawText
64 | $sexpression
65 | $response
66 | $error
67 | |j};
68 | };
69 | };
70 |
71 | type t = array(Entry.t);
72 |
73 | let createEntry = (cmd, log) => {
74 | let entry = Entry.make(cmd);
75 | Js.Array.push(entry, log) |> ignore;
76 | };
77 |
78 | let updateLatestEntry = (f: Entry.t => unit, log) => {
79 | let n = Array.length(log);
80 | let lastEntry = log[n - 1];
81 | lastEntry->Option.forEach(f);
82 | };
83 |
84 | let logRawText = text =>
85 | updateLatestEntry(entry =>
86 | Js.Array.push(text, entry.response.rawText) |> ignore
87 | );
88 |
89 | let logSExpression = text =>
90 | updateLatestEntry(entry =>
91 | Js.Array.push(text, entry.response.sexpression) |> ignore
92 | );
93 |
94 | let logResponse = text =>
95 | updateLatestEntry(entry =>
96 | Js.Array.push(text, entry.response.response) |> ignore
97 | );
98 |
99 | let logError = text =>
100 | updateLatestEntry(log => Js.Array.push(text, log.response.error) |> ignore);
101 |
102 | let serialize = x =>
103 | x->Array.mapWithIndex(Entry.serialize)->Js.String.concatMany("\n");
--------------------------------------------------------------------------------
/lib/js/src/Editors.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Belt_Option = require("bs-platform/lib/js/belt_Option.js");
5 | var Caml_option = require("bs-platform/lib/js/caml_option.js");
6 |
7 | function make(editor) {
8 | return {
9 | focused: /* Source */0,
10 | source: editor,
11 | query: undefined
12 | };
13 | }
14 |
15 | function getID(self) {
16 | return String(self.source.id);
17 | }
18 |
19 | function get(editors) {
20 | var match = editors.focused;
21 | if (match) {
22 | var match$1 = editors.query;
23 | if (match$1 !== undefined) {
24 | return Caml_option.valFromOption(match$1);
25 | } else {
26 | return editors.source;
27 | }
28 | } else {
29 | return editors.source;
30 | }
31 | }
32 |
33 | function on(sort, editors) {
34 | if (sort) {
35 | Belt_Option.forEach(Belt_Option.map(editors.query, (function (prim) {
36 | return atom.views.getView(prim);
37 | })), (function (prim) {
38 | prim.focus();
39 | return /* () */0;
40 | }));
41 | editors.focused = /* Query */1;
42 | return /* () */0;
43 | } else {
44 | atom.views.getView(editors.source).focus();
45 | editors.focused = /* Source */0;
46 | return /* () */0;
47 | }
48 | }
49 |
50 | var Focus = {
51 | get: get,
52 | on: on
53 | };
54 |
55 | function getSymbol(editors) {
56 | var arg = get(editors).getSelectedText();
57 | return (function (param) {
58 | return (function (param$1) {
59 | return arg.substr(param, param$1);
60 | });
61 | })(0)(1);
62 | }
63 |
64 | function getTextNode(editors) {
65 | var getLargerSyntaxNode = function (param) {
66 | get(editors).selectLargerSyntaxNode();
67 | return get(editors).getSelectedText();
68 | };
69 | var getPointedWord = function (param) {
70 | get(editors).selectWordsContainingCursors();
71 | return get(editors).getSelectedText();
72 | };
73 | var selectedText = get(editors).getSelectedText();
74 | if (selectedText === "") {
75 | var largerNode = getLargerSyntaxNode(/* () */0);
76 | if (largerNode === "") {
77 | return getPointedWord(/* () */0);
78 | } else {
79 | var pointedText = getPointedWord(/* () */0);
80 | if (pointedText === "_") {
81 | return getLargerSyntaxNode(/* () */0);
82 | } else {
83 | return pointedText;
84 | }
85 | }
86 | } else {
87 | return selectedText;
88 | }
89 | }
90 |
91 | var $$Selection = {
92 | getSymbol: getSymbol,
93 | getTextNode: getTextNode
94 | };
95 |
96 | exports.make = make;
97 | exports.getID = getID;
98 | exports.Focus = Focus;
99 | exports.$$Selection = $$Selection;
100 | /* No side effect */
101 |
--------------------------------------------------------------------------------
/src/View/Emacs/Emacs__GoalTypeContext.re:
--------------------------------------------------------------------------------
1 | open ReasonReact;
2 |
3 | open Rebase;
4 | open Rebase.Option;
5 |
6 | open Emacs__Component;
7 |
8 | type t = {
9 | goal: option(Expr.t),
10 | have: option(Expr.t),
11 | interactionMetas: array(Output.t),
12 | hiddenMetas: array(Output.t),
13 | };
14 | let parse = raw => {
15 | let markGoal = ((line, _)) =>
16 | line |> Js.String.match([%re "/^Goal:/"]) |> map(_ => "goal");
17 | let markHave = ((line, _)) =>
18 | line |> Js.String.match([%re "/^Have:/"]) |> map(_ => "have");
19 | let markMetas = ((line, _)) =>
20 | line |> Js.String.match([%re "/\\u2014{60}/g"]) |> map(_ => "metas");
21 | let partiteGoalTypeContext =
22 | Util.Dict.partite(line =>
23 | or_(or_(markGoal(line), markHave(line)), markMetas(line))
24 | );
25 | let removeDelimeter = Util.Dict.update("metas", Js.Array.sliceFrom(1));
26 | let lines = raw |> Js.String.split("\n");
27 | let dictionary =
28 | lines
29 | |> partiteGoalTypeContext
30 | |> removeDelimeter
31 | |> Emacs__Parser.partiteMetas;
32 | /* extract entries from the dictionary */
33 | let goal =
34 | "goal"
35 | |> Js.Dict.get(dictionary)
36 | |> flatMap(line =>
37 | line
38 | |> List.fromArray
39 | |> String.joinWith("\n")
40 | |> Js.String.sliceToEnd(~from=5)
41 | |> Expr.parse
42 | );
43 | let have =
44 | "have"
45 | |> Js.Dict.get(dictionary)
46 | |> flatMap(line =>
47 | line
48 | |> List.fromArray
49 | |> String.joinWith("\n")
50 | |> Js.String.sliceToEnd(~from=5)
51 | |> Expr.parse
52 | );
53 | let interactionMetas =
54 | "interactionMetas"
55 | |> Js.Dict.get(dictionary)
56 | |> mapOr(
57 | metas =>
58 | metas
59 | |> Array.map(Output.parseOutputWithoutRange)
60 | |> Array.filterMap(x => x),
61 | [||],
62 | );
63 | let hiddenMetas =
64 | "hiddenMetas"
65 | |> Js.Dict.get(dictionary)
66 | |> mapOr(
67 | metas =>
68 | metas
69 | |> Array.map(Output.parseOutputWithRange)
70 | |> Array.filterMap(x => x),
71 | [||],
72 | );
73 | {goal, have, interactionMetas, hiddenMetas};
74 | };
75 |
76 | [@react.component]
77 | let make = (~body: string) => {
78 | let parsed = parse(body);
79 | <>
80 |
81 | {parsed.goal
82 | |> Option.mapOr(expr => , null)}
83 | {parsed.have
84 | |> Option.mapOr(expr => , null)}
85 |
86 | {
87 | {parsed.interactionMetas
88 | |> Array.mapi((value, i) => )
89 | |> React.array}
90 |
}
91 | {
92 | {parsed.hiddenMetas
93 | |> Array.mapi((value, i) => )
94 | |> React.array}
95 |
}
96 | >;
97 | };
--------------------------------------------------------------------------------
/src/View/Panel/InputMethod/Extension.re:
--------------------------------------------------------------------------------
1 | // For adding or prioritizing key mapping
2 | open Rebase;
3 | open Fn;
4 | open Json.Decode;
5 |
6 | type keymap = Js.Dict.t(array(string));
7 |
8 | let defaultKeymap = () => {
9 | let keymap = Js.Dict.empty();
10 | Js.Dict.set(keymap, "^r", [|{js|ʳ|js}|]);
11 | Js.Dict.set(keymap, "^l", [|{js|ˡ|js}|]);
12 | keymap;
13 | };
14 |
15 | let readConfig = () => {
16 | switch (
17 | Js.undefinedToOption(Atom.Config.get("agda-mode.inputMethodExtension"))
18 | ) {
19 | | None => "{}"
20 | | Some(s) => s
21 | };
22 | };
23 |
24 | let setConfig = keymap => {
25 | open! Json.Encode;
26 |
27 | let encoder: keymap => Js.Json.t = dict(array(string));
28 |
29 | Atom.Config.set(
30 | "agda-mode.inputMethodExtension",
31 | Json.stringify(encoder(keymap)),
32 | )
33 | |> ignore;
34 | };
35 |
36 | let parse: string => option(keymap) =
37 | Json.parse >> Option.map(dict(array(string)));
38 |
39 | let readKeymap = readConfig >> parse >> Option.getOr(Js.Dict.empty());
40 |
41 | let lookup = key => Js.Dict.get(readKeymap(), key) |> Option.getOr([||]);
42 |
43 | let add = (key, symbols) => {
44 | let keymap = readKeymap();
45 | switch (Js.Dict.get(keymap, key)) {
46 | | None => Js.Dict.set(keymap, key, symbols)
47 | | Some(existing) =>
48 | // put the existing symbols in the back
49 | Js.Dict.set(keymap, key, Array.concat(existing, symbols))
50 | };
51 |
52 | setConfig(keymap);
53 | };
54 |
55 | let modify = (key, symbols) => {
56 | let keymap = readKeymap();
57 | Js.Dict.set(keymap, key, symbols);
58 | setConfig(keymap);
59 | };
60 |
61 | let delete = key => {
62 | let keymap = readKeymap();
63 | let delete': string => unit = [%raw
64 | "function (id) {
65 | delete keymap[id]
66 | }"
67 | ];
68 | delete'(key);
69 | setConfig(keymap);
70 | };
71 |
72 | let resetToDefault = () => {
73 | setConfig(defaultKeymap());
74 | };
75 |
76 | let extendKeySuggestions = (key, origionalSuggestions) => {
77 | let extension =
78 | readKeymap()
79 | |> Js.Dict.keys
80 | |> Array.filter(String.startsWith(key))
81 | |> Array.map(String.sub(~from=String.length(key), ~length=1))
82 | |> Array.filter(s => String.length(s) === 1);
83 |
84 | // no repeated elements
85 | let union = (xs, ys) => {
86 | ys |> Array.filter(y => !Js.Array.includes(y, xs)) |> Array.concat(xs);
87 | };
88 |
89 | union(origionalSuggestions, extension) |> Js.Array.sortInPlace;
90 | };
91 |
92 | // if the extened symbols are prioritized over the origional ones
93 | let extendCandidateSymbols = (key, originalCandidates) => {
94 | let extension = lookup(key);
95 |
96 | let removedOverlaps =
97 | originalCandidates |> Array.filter(s => !Js.Array.includes(s, extension)); // remove the overlapping symbols
98 | // prefix the extension symbols
99 | Array.concat(removedOverlaps, extension);
100 | };
--------------------------------------------------------------------------------
/src/Instance/Instance__Highlightings.re:
--------------------------------------------------------------------------------
1 | open! Rebase;
2 |
3 | open Instance__Type;
4 |
5 | open Task.Highlightings;
6 | open! Atom;
7 |
8 | /* lots of side effects! */
9 | let add = (annotation: Highlighting.Annotation.t, instance) => {
10 | let textBuffer = TextEditor.getBuffer(instance.editors.source);
11 | let startPoint =
12 | textBuffer |> TextBuffer.positionForCharacterIndex(annotation.start - 1);
13 | let endPoint =
14 | textBuffer |> TextBuffer.positionForCharacterIndex(annotation.end_ - 1);
15 | let range = Range.make(startPoint, endPoint);
16 | let marker = TextEditor.markBufferRange(range, instance.editors.source);
17 | /* update the state */
18 | instance.highlightings |> Js.Array.push(marker) |> ignore;
19 | /* decorate */
20 | let types = annotation.types |> Js.Array.joinWith(" ");
21 |
22 | TextEditor.decorateMarker(
23 | marker,
24 | TextEditor.decorateMarkerOptions(
25 | ~type_="highlight",
26 | ~class_="highlight-decoration " ++ types,
27 | (),
28 | ),
29 | instance.editors.source,
30 | )
31 | |> ignore;
32 | };
33 |
34 | let addMany = (annotations, instance) =>
35 | annotations
36 | |> Array.filter(Highlighting.Annotation.shouldHighlight)
37 | |> Array.forEach(annotation => instance |> add(annotation));
38 |
39 | let addFromFile = (filepath, instance): Promise.t(unit) => {
40 | let readFile = N.Fs.readFile |> N.Util.promisify;
41 | /* read and parse and add */
42 | readFile(. filepath)
43 | ->Promise.Js.fromBsPromise
44 | ->Promise.Js.toResult
45 | ->Promise.map(
46 | fun
47 | | Ok(content) => {
48 | open! Parser__Type.SExpression;
49 | content
50 | |> Node.Buffer.toString
51 | |> Parser.SExpression.parse
52 | |> Array.filterMap(Option.fromResult) // throwing away errors
53 | |> Array.map(tokens =>
54 | switch (tokens) {
55 | | L(xs) =>
56 | xs |> Highlighting.Annotation.parseIndirectHighlightings
57 | | _ => [||]
58 | }
59 | )
60 | |> Array.flatMap(x => x)
61 | |> Array.filter(Highlighting.Annotation.shouldHighlight)
62 | |> Array.forEach(annotation => instance |> add(annotation));
63 | ();
64 | }
65 | | Error(err) => {
66 | Js.log(err);
67 | Js.log("cannot read the indirect highlighting file: " ++ filepath);
68 | },
69 | );
70 | };
71 |
72 | let destroyAll = instance => {
73 | instance.highlightings |> Array.forEach(DisplayMarker.destroy);
74 | instance.highlightings = [||];
75 | };
76 |
77 | let execute = instance =>
78 | fun
79 | | AddDirectly(annotations) => {
80 | addMany(annotations, instance);
81 | Promise.resolved([||]);
82 | }
83 | | AddIndirectly(filepath) => {
84 | // read the file
85 | addFromFile(filepath, instance)
86 | // delete the file
87 | ->Promise.map(() => Ok(N.Fs.unlink(filepath, _ => ())))
88 | ->Promise.map(_ => [||]);
89 | };
90 |
--------------------------------------------------------------------------------
/lib/js/src/View/Component__Link.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Curry = require("bs-platform/lib/js/curry.js");
5 | var React = require("react");
6 | var $$String = require("bs-platform/lib/js/string.js");
7 | var Type__View$AgdaMode = require("../Type/Type__View.bs.js");
8 | var Caml_chrome_debugger = require("bs-platform/lib/js/caml_chrome_debugger.js");
9 |
10 | function Component__Link(Props) {
11 | var targetOpt = Props.target;
12 | var jumpOpt = Props.jump;
13 | var hoverOpt = Props.hover;
14 | var classNameOpt = Props.className;
15 | var children = Props.children;
16 | var target = targetOpt !== undefined ? targetOpt : /* RangeLink */Caml_chrome_debugger.variant("RangeLink", 0, [/* NoRange */0]);
17 | var jump = jumpOpt !== undefined ? jumpOpt : false;
18 | var hover = hoverOpt !== undefined ? hoverOpt : false;
19 | var className = classNameOpt !== undefined ? classNameOpt : /* [] */0;
20 | var target_;
21 | if (target.tag) {
22 | target_ = /* HoleLink */Caml_chrome_debugger.variant("HoleLink", 1, [target[0]]);
23 | } else {
24 | var range = target[0];
25 | target_ = range && range[1].length !== 0 ? /* RangeLink */Caml_chrome_debugger.variant("RangeLink", 0, [range]) : undefined;
26 | }
27 | var emit = React.useContext(Type__View$AgdaMode.Mouse.emitter);
28 | if (target_ !== undefined) {
29 | var t = target_;
30 | return React.createElement("span", {
31 | className: $$String.concat(" ", /* :: */Caml_chrome_debugger.simpleVariant("::", [
32 | "link",
33 | className
34 | ])),
35 | onClick: (function (param) {
36 | if (jump) {
37 | return Curry._1(emit, /* JumpToTarget */Caml_chrome_debugger.variant("JumpToTarget", 0, [t]));
38 | } else {
39 | return 0;
40 | }
41 | }),
42 | onMouseOut: (function (param) {
43 | if (hover) {
44 | return Curry._1(emit, /* MouseOut */Caml_chrome_debugger.variant("MouseOut", 2, [t]));
45 | } else {
46 | return 0;
47 | }
48 | }),
49 | onMouseOver: (function (param) {
50 | if (hover) {
51 | return Curry._1(emit, /* MouseOver */Caml_chrome_debugger.variant("MouseOver", 1, [t]));
52 | } else {
53 | return 0;
54 | }
55 | })
56 | }, children);
57 | } else {
58 | return React.createElement("span", {
59 | className: $$String.concat(" ", /* :: */Caml_chrome_debugger.simpleVariant("::", [
60 | "link",
61 | className
62 | ]))
63 | }, children);
64 | }
65 | }
66 |
67 | var make = Component__Link;
68 |
69 | exports.make = make;
70 | /* react Not a pure module */
71 |
--------------------------------------------------------------------------------
/src/View/Panel/InputMethod/Buffer.re:
--------------------------------------------------------------------------------
1 | open Rebase;
2 |
3 | type t = {
4 | // the symbol at the front of the sequence along with the sequence it replaced
5 | symbol: option((string, string)),
6 | // the sequence following the symbol we see on the text editor
7 | tail: string,
8 | };
9 |
10 | // examples of Buffer.t:
11 | // user typed: l => { symbol: Some("←", "l"), tail: "" }
12 | // user typed: lambd => { symbol: Some("←", "lambd"), tail: "ambd" }
13 | // user typed: lambda => { symbol: Some("λ", "lambda"), tail: "" }
14 |
15 | let init = string =>
16 | Js.String.substring(~from=0, ~to_=String.length(string) - 1, string);
17 |
18 | let initial = {symbol: None, tail: ""};
19 |
20 | let isEmpty = self => {
21 | self.symbol == None && String.isEmpty(self.tail);
22 | };
23 |
24 | let toSequence = self =>
25 | switch (self.symbol) {
26 | | None => self.tail
27 | | Some((_, sequence)) => sequence ++ self.tail
28 | };
29 |
30 | let toSurface = self =>
31 | switch (self.symbol) {
32 | | None => self.tail
33 | | Some((symbol, _)) => symbol ++ self.tail
34 | };
35 |
36 | let toString = self =>
37 | "\"" ++ toSurface(self) ++ "\"[" ++ toSequence(self) ++ "]";
38 |
39 | type action =
40 | | Noop
41 | | Insert(t) // update the buffer accordingly
42 | | Backspace(t) // update the buffer accordingly
43 | | Rewrite(t) // should rewrite the text buffer
44 | | Complete // should deactivate
45 | | Stuck(int); // should deactivate, too
46 |
47 | // devise the next state
48 | let next = (self, reality) => {
49 | let surface = toSurface(self);
50 | let sequence = toSequence(self);
51 | if (reality == surface) {
52 | if (Translator.translate(sequence).further && reality != "\\") {
53 | Noop;
54 | } else {
55 | Complete;
56 | };
57 | } else if (init(reality) == surface) {
58 | // insertion
59 | let insertedChar = Js.String.substr(~from=-1, reality);
60 | let sequence' = sequence ++ insertedChar;
61 | let translation = Translator.translate(sequence');
62 | switch (translation.symbol) {
63 | | Some(symbol) =>
64 | if (insertedChar == symbol && insertedChar == "\\") {
65 | Stuck(0);
66 | } else {
67 | Rewrite({symbol: Some((symbol, sequence')), tail: ""});
68 | }
69 | | None =>
70 | if (translation.further) {
71 | Insert({...self, tail: self.tail ++ insertedChar});
72 | } else {
73 | Stuck(1);
74 | }
75 | };
76 | } else if (reality == init(surface) || reality == init(sequence)) {
77 | // backspace deletion
78 | if (String.isEmpty(reality)) {
79 | // if the symbol is gone
80 | if (Option.isSome(self.symbol)) {
81 | // A symbol has just been backspaced and gone
82 | // replace it with the underlying sequence (backspaced)
83 | Rewrite({
84 | symbol: None,
85 | tail: init(sequence),
86 | });
87 | } else {
88 | Stuck(2);
89 | };
90 | } else {
91 | // normal backspace
92 | Backspace({...self, tail: init(self.tail)});
93 | };
94 | } else {
95 | Stuck(3);
96 | };
97 | };
98 |
--------------------------------------------------------------------------------
/test/Test__Main.re:
--------------------------------------------------------------------------------
1 | open! BsMocha.Mocha;
2 | open! BsMocha.Promise;
3 | open Js.Promise;
4 |
5 | open Test__Util;
6 |
7 | before(Package.activate);
8 | after(Package.deactivate);
9 |
10 | // describe("Package", () => {
11 | // it(
12 | // "should be activated after triggering 'agda-mode:load' on .agda files", () => {
13 | // // before
14 | // expect("agda-mode")
15 | // |> Modifiers.not
16 | // |> to_be_one_of(Package.activeNames())
17 | // |> ignore;
18 | //
19 | // File.openAsset("Temp.agda")
20 | // |> then_(dispatch("agda-mode:load"))
21 | // |> then_(_
22 | // // after
23 | // =>
24 | // expect("agda-mode")
25 | // |> to_be_one_of(Package.activeNames())
26 | // |> resolve
27 | // );
28 | // });
29 | //
30 | // it(
31 | // "should be activated after triggering 'agda-mode:load' on .lagda files", () => {
32 | // // before
33 | // expect("agda-mode")
34 | // |> Modifiers.not
35 | // |> to_be_one_of(Package.activeNames())
36 | // |> ignore;
37 | //
38 | // File.openAsset("Blank2.lagda")
39 | // |> then_(dispatch("agda-mode:load"))
40 | // |> then_(_
41 | // // after
42 | // =>
43 | // expect("agda-mode")
44 | // |> to_be_one_of(Package.activeNames())
45 | // |> resolve
46 | // );
47 | // });
48 | // });
49 |
50 | describe("Instances", () => {
51 | it_skip("should have no instances before opening any files", () =>
52 | AgdaMode.Instances.size() |> Assert.equal(0) |> resolve
53 | );
54 |
55 | it("should respect the number of opened .agda file", () =>
56 | File.openAsset("Temp.agda")
57 | |> then_(editor => {
58 | AgdaMode.Instances.size() |> Assert.equal(1);
59 | let pane = Atom.Workspace.getActivePane();
60 | Atom.Pane.destroyItem_(
61 | Atom.TextEditor.asWorkspaceItem(editor),
62 | true,
63 | pane,
64 | );
65 | })
66 | |> then_(destroyed => {
67 | Assert.equal(AgdaMode.Instances.size(), destroyed ? 0 : 1);
68 | resolve();
69 | })
70 | );
71 |
72 | it("should respect the number of opened .lagda file", () =>
73 | File.openAsset("Blank2.lagda")
74 | |> then_(editor => {
75 | AgdaMode.Instances.size() |> Assert.equal(1);
76 | let pane = Atom.Workspace.getActivePane();
77 | Atom.Pane.destroyItem_(
78 | Atom.TextEditor.asWorkspaceItem(editor),
79 | true,
80 | pane,
81 | );
82 | })
83 | |> then_(destroyed => {
84 | AgdaMode.Instances.size() |> Assert.equal(destroyed ? 0 : 1);
85 | resolve();
86 | })
87 | );
88 |
89 | it("should include '.agda' in the classlist", () =>
90 | File.openAsset("Temp.agda")
91 | |> then_(editor => {
92 | editor
93 | |> Atom.Views.getView
94 | |> Webapi.Dom.HtmlElement.classList
95 | |> Webapi.Dom.DomTokenList.contains("agda")
96 | |> Assert.yes;
97 | resolve();
98 | })
99 | );
100 | });
101 |
--------------------------------------------------------------------------------
/src/Highlighting.re:
--------------------------------------------------------------------------------
1 | open Belt;
2 |
3 | type removeTokenBasedHighlighting =
4 | | Remove
5 | | Keep;
6 |
7 | type filepath = string;
8 | module Token = Parser.SExpression;
9 |
10 | module Annotation = {
11 | open Token;
12 | type t = {
13 | start: int,
14 | end_: int,
15 | types: array(string),
16 | source: option((filepath, int)),
17 | };
18 | let toString = self =>
19 | "Annotation "
20 | ++ string_of_int(self.start)
21 | ++ " "
22 | ++ string_of_int(self.end_)
23 | ++ " "
24 | ++ Util.Pretty.list(List.fromArray(self.types))
25 | ++ (
26 | switch (self.source) {
27 | | None => ""
28 | | Some((s, i)) => s ++ " " ++ string_of_int(i)
29 | }
30 | );
31 | let parse: Token.t => option(t) =
32 | fun
33 | | A(_) => None
34 | | L(xs) =>
35 | switch (xs) {
36 | | [|
37 | A(start'),
38 | A(end_'),
39 | types,
40 | _,
41 | _,
42 | L([|A(filepath), _, A(index')|]),
43 | |] =>
44 | Parser.int(start')
45 | ->Option.flatMap(start =>
46 | Parser.int(end_')
47 | ->Option.flatMap(end_ =>
48 | Parser.int(index')
49 | ->Option.map(index =>
50 | {
51 | start,
52 | end_,
53 | types: flatten(types),
54 | source: Some((filepath, index)),
55 | }
56 | )
57 | )
58 | )
59 |
60 | | [|A(start'), A(end_'), types|] =>
61 | Parser.int(start')
62 | ->Option.flatMap(start =>
63 | Parser.int(end_')
64 | ->Option.map(end_ =>
65 | {start, end_, types: flatten(types), source: None}
66 | )
67 | )
68 | | [|A(start'), A(end_'), types, _|] =>
69 | Parser.int(start')
70 | ->Option.flatMap(start =>
71 | Parser.int(end_')
72 | ->Option.map(end_ =>
73 | {start, end_, types: flatten(types), source: None}
74 | )
75 | )
76 | | _ => None
77 | };
78 | let parseDirectHighlightings: array(Token.t) => array(t) =
79 | tokens => {
80 | tokens
81 | ->Js.Array.sliceFrom(2, _)
82 | ->Array.map(parse)
83 | ->Array.keepMap(x => x);
84 | };
85 | let parseIndirectHighlightings: array(Token.t) => array(t) =
86 | tokens =>
87 | tokens
88 | ->Js.Array.sliceFrom(1, _)
89 | ->Array.map(parse)
90 | ->Array.keepMap(x => x);
91 | /* the type of annotations that we want to highlight */
92 | let shouldHighlight: t => bool =
93 | annotation => {
94 | annotation.types
95 | |> Js.Array.includes("unsolvedmeta")
96 | || annotation.types
97 | |> Js.Array.includes("unsolvedconstraint")
98 | || annotation.types
99 | |> Js.Array.includes("terminationproblem")
100 | || annotation.types
101 | |> Js.Array.includes("coverageproblem");
102 | };
103 | };
104 |
105 | type t = Atom.DisplayMarker.t;
--------------------------------------------------------------------------------
/lib/js/test/Test__Main.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Mocha$BsMocha = require("bs-mocha/lib/js/src/Mocha.bs.js");
5 | var Promise$BsMocha = require("bs-mocha/lib/js/src/Promise.bs.js");
6 | var AgdaMode$AgdaMode = require("../src/AgdaMode.bs.js");
7 | var Test__Util$AgdaMode = require("./Test__Util.bs.js");
8 |
9 | Promise$BsMocha.before(undefined)(undefined, undefined, undefined, Test__Util$AgdaMode.Package.activate);
10 |
11 | Promise$BsMocha.after(undefined)(undefined, undefined, undefined, Test__Util$AgdaMode.Package.deactivate);
12 |
13 | Mocha$BsMocha.describe("Instances")(undefined, undefined, undefined, (function (param) {
14 | Promise$BsMocha.it_skip("should have no instances before opening any files")(undefined, undefined, undefined, (function (param) {
15 | return Promise.resolve(Test__Util$AgdaMode.Assert.equal(undefined, 0, AgdaMode$AgdaMode.Instances.size(/* () */0)));
16 | }));
17 | Promise$BsMocha.it("should respect the number of opened .agda file")(undefined, undefined, undefined, (function (param) {
18 | return Test__Util$AgdaMode.$$File.openAsset("Temp.agda").then((function (editor) {
19 | Test__Util$AgdaMode.Assert.equal(undefined, 1, AgdaMode$AgdaMode.Instances.size(/* () */0));
20 | var pane = atom.workspace.getActivePane();
21 | return pane.destroyItem(editor, true);
22 | })).then((function (destroyed) {
23 | Test__Util$AgdaMode.Assert.equal(undefined, AgdaMode$AgdaMode.Instances.size(/* () */0), destroyed ? 0 : 1);
24 | return Promise.resolve(/* () */0);
25 | }));
26 | }));
27 | Promise$BsMocha.it("should respect the number of opened .lagda file")(undefined, undefined, undefined, (function (param) {
28 | return Test__Util$AgdaMode.$$File.openAsset("Blank2.lagda").then((function (editor) {
29 | Test__Util$AgdaMode.Assert.equal(undefined, 1, AgdaMode$AgdaMode.Instances.size(/* () */0));
30 | var pane = atom.workspace.getActivePane();
31 | return pane.destroyItem(editor, true);
32 | })).then((function (destroyed) {
33 | Test__Util$AgdaMode.Assert.equal(undefined, destroyed ? 0 : 1, AgdaMode$AgdaMode.Instances.size(/* () */0));
34 | return Promise.resolve(/* () */0);
35 | }));
36 | }));
37 | return Promise$BsMocha.it("should include '.agda' in the classlist")(undefined, undefined, undefined, (function (param) {
38 | return Test__Util$AgdaMode.$$File.openAsset("Temp.agda").then((function (editor) {
39 | Test__Util$AgdaMode.Assert.yes(atom.views.getView(editor).classList.contains("agda"));
40 | return Promise.resolve(/* () */0);
41 | }));
42 | }));
43 | }));
44 |
45 | /* Not a pure module */
46 |
--------------------------------------------------------------------------------
/src/View/Panel/InputMethod/CandidateSymbols.re:
--------------------------------------------------------------------------------
1 | open ReasonReact;
2 | open Util.React;
3 |
4 | open Rebase;
5 |
6 | open Webapi;
7 |
8 | type action =
9 | | Up
10 | | Down
11 | | Right
12 | | Left;
13 |
14 | let reducer = (totalSize, index, action) =>
15 | switch (action) {
16 | | Up => max(0, index - 10)
17 | | Right => min(totalSize - 1, index + 1)
18 | | Down => min(totalSize - 1, index + 10)
19 | | Left => max(0, index - 1)
20 | };
21 |
22 | [@react.component]
23 | let make =
24 | (
25 | ~activated: bool,
26 | ~candidateSymbols: array(string),
27 | ~updateTranslation: option(string) => unit,
28 | ~chooseSymbol: string => unit,
29 | ) => {
30 | let (index, move) =
31 | React.useReducer(reducer(Array.length(candidateSymbols)), 0);
32 |
33 | Hook.useAtomListenerWhen(
34 | () =>
35 | Atom.Commands.add(
36 | `CSSSelector("atom-text-editor.agda-mode-input-method-activated"),
37 | "core:move-up",
38 | event => {
39 | move(Up);
40 | event |> Dom.Event.stopImmediatePropagation;
41 | },
42 | ),
43 | activated,
44 | );
45 |
46 | Hook.useAtomListenerWhen(
47 | () =>
48 | Atom.Commands.add(
49 | `CSSSelector("atom-text-editor.agda-mode-input-method-activated"),
50 | "core:move-right",
51 | event => {
52 | move(Right);
53 | event |> Dom.Event.stopImmediatePropagation;
54 | },
55 | ),
56 | activated,
57 | );
58 |
59 | Hook.useAtomListenerWhen(
60 | () =>
61 | Atom.Commands.add(
62 | `CSSSelector("atom-text-editor.agda-mode-input-method-activated"),
63 | "core:move-down",
64 | event => {
65 | move(Down);
66 | event |> Dom.Event.stopImmediatePropagation;
67 | },
68 | ),
69 | activated,
70 | );
71 |
72 | Hook.useAtomListenerWhen(
73 | () =>
74 | Atom.Commands.add(
75 | `CSSSelector("atom-text-editor.agda-mode-input-method-activated"),
76 | "core:move-left",
77 | event => {
78 | move(Left);
79 | event |> Dom.Event.stopImmediatePropagation;
80 | },
81 | ),
82 | activated,
83 | );
84 |
85 | React.useEffect1(
86 | () => {
87 | updateTranslation(candidateSymbols[index]);
88 | None;
89 | },
90 | [|index|],
91 | );
92 |
93 | let rowStart = index / 10 * 10;
94 | let row =
95 | candidateSymbols |> Array.slice(~from=rowStart, ~to_=rowStart + 10);
96 | switch (candidateSymbols[index]) {
97 | | None => null
98 | | Some(_) =>
99 | row
100 | |> Array.mapi((key, i) => {
101 | let isSelected = rowStart + i === index;
102 | ;
108 | })
109 | |> ReactDOMRe.createDOMElementVariadic(
110 | "div",
111 | ~props=
112 | ReactDOMRe.domProps(
113 | ~className="candidates btn-group btn-group-sm",
114 | (),
115 | ),
116 | )
117 | };
118 | };
119 |
--------------------------------------------------------------------------------
/src/View/View.re:
--------------------------------------------------------------------------------
1 | open Type.View;
2 |
3 | /************************************************************************************************************/
4 |
5 | type t = {
6 | // related
7 | activate: unit => Promise.t(unit),
8 | deactivate: unit => Promise.t(unit),
9 | toggleDocking: unit => Promise.t(unit),
10 | display: (string, Type.View.Header.style, Body.t) => Promise.t(unit),
11 | inquire:
12 | (string, string, string) =>
13 | Promise.t(Rebase.result(string, MiniEditor.error)),
14 | updateIsPending: bool => Promise.t(unit),
15 | destroy: unit => Promise.t(unit),
16 | onDestroy: Event.t(unit),
17 | onMouseEvent: Event.t(Mouse.event),
18 | // related
19 | activateInputMethod: bool => Promise.t(unit),
20 | interceptAndInsertKey: string => Promise.t(unit),
21 | onInputMethodChange: Event.t(InputMethod.state),
22 | // related
23 | navigateSettings: Settings__Breadcrumb.uri => Promise.t(unit),
24 | // related
25 | updateConnection:
26 | (option(Connection.t), option(Connection.Error.t)) => Promise.t(unit),
27 | inquireConnection:
28 | unit => Promise.t(Rebase.result(string, MiniEditor.error)),
29 | };
30 | let make = (events: Events.t, channels: Channels.t) => {
31 | let activate = () => channels.activatePanel |> Channel.send();
32 | let deactivate = () => channels.deactivatePanel |> Channel.send();
33 | let toggleDocking = () => channels.toggleDocking |> Channel.send();
34 |
35 | let display = (text, style, body) =>
36 | channels.display |> Channel.send(({Type.View.Header.text, style}, body));
37 |
38 | let inquire = (text, placeholder, value) =>
39 | activate()
40 | ->Promise.flatMap(_ =>
41 | channels.inquire
42 | |> Channel.send((
43 | {Type.View.Header.text, style: PlainText},
44 | placeholder,
45 | value,
46 | ))
47 | );
48 |
49 | let updateIsPending = isPending =>
50 | channels.updateIsPending |> Channel.send(isPending);
51 |
52 | let onMouseEvent = events.onMouseEvent;
53 |
54 | let activateInputMethod = activate =>
55 | channels.activateInputMethod |> Channel.send(activate);
56 |
57 | let interceptAndInsertKey = symbol =>
58 | channels.interceptAndInsertKey |> Channel.send(symbol);
59 |
60 | let onInputMethodChange = events.onInputMethodChange;
61 |
62 | let navigateSettings = where =>
63 | channels.navigateSettings |> Channel.send(Some(where));
64 |
65 | let updateConnection = (connection, error) =>
66 | channels.updateConnection |> Channel.send((connection, error));
67 |
68 | let inquireConnection = () => channels.inquireConnection |> Channel.send();
69 |
70 | let onDestroy = Event.make();
71 | let destroy = () =>
72 | deactivate()
73 | ->Promise.flatMap(_ => activateInputMethod(false))
74 | ->Promise.flatMap(_ => channels.destroy |> Channel.send())
75 | ->Promise.tap(_ => onDestroy.emit());
76 |
77 | {
78 | activate,
79 | deactivate,
80 | display,
81 | inquire,
82 | updateIsPending,
83 | destroy,
84 | onDestroy,
85 | onMouseEvent,
86 | activateInputMethod,
87 | interceptAndInsertKey,
88 | onInputMethodChange,
89 | navigateSettings,
90 | updateConnection,
91 | inquireConnection,
92 | toggleDocking,
93 | };
94 | };
95 |
--------------------------------------------------------------------------------
/lib/js/src/View/Panel/InputMethod/Translator.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Rebase = require("@glennsl/rebase/lib/js/src/Rebase.bs.js");
5 | var Js_dict = require("bs-platform/lib/js/js_dict.js");
6 | var Extension$AgdaMode = require("./Extension.bs.js");
7 | var QueryJs = require("./../../../../../../asset/query.js");
8 | var KeymapJs = require("./../../../../../../asset/keymap.js");
9 |
10 | var rawTable = QueryJs.default;
11 |
12 | var rawKeymapObject = KeymapJs.default;
13 |
14 | function fromObject(obj) {
15 | var symbol = (obj[">>"] || []);
16 | var subTrie = Js_dict.fromArray(Rebase.$$Array.map((function (key) {
17 | return /* tuple */[
18 | key,
19 | fromObject((obj[key]))
20 | ];
21 | }), Rebase.$$Array.filter((function (key) {
22 | return key !== ">>";
23 | }), Object.keys(obj))));
24 | return {
25 | symbol: symbol,
26 | subTrie: subTrie
27 | };
28 | }
29 |
30 | var keymap = fromObject(rawKeymapObject);
31 |
32 | function toKeySuggestions(trie) {
33 | return Object.keys(trie.subTrie);
34 | }
35 |
36 | function toCandidateSymbols(trie) {
37 | return trie.symbol;
38 | }
39 |
40 | function isInKeymap(input) {
41 | var _input = input;
42 | var _trie = keymap;
43 | while(true) {
44 | var trie = _trie;
45 | var input$1 = _input;
46 | var n = Rebase.$$String.length(input$1);
47 | if (n !== 0) {
48 | var key = Rebase.$$String.sub(0, 1, input$1);
49 | var rest = Rebase.$$String.sub(1, n - 1 | 0, input$1);
50 | var match = Js_dict.get(trie.subTrie, key);
51 | if (match !== undefined) {
52 | _trie = match;
53 | _input = rest;
54 | continue ;
55 | } else {
56 | return ;
57 | }
58 | } else {
59 | return trie;
60 | }
61 | };
62 | }
63 |
64 | function translate(input) {
65 | var trie = isInKeymap(input);
66 | var keySuggestions = Extension$AgdaMode.extendKeySuggestions(input, Rebase.$$Option.mapOr(toKeySuggestions, [], trie));
67 | var candidateSymbols = Extension$AgdaMode.extendCandidateSymbols(input, Rebase.$$Option.mapOr(toCandidateSymbols, [], trie));
68 | return {
69 | symbol: Rebase.$$Array.get(candidateSymbols, 0),
70 | further: Rebase.$$Array.length(keySuggestions) !== 0,
71 | keySuggestions: keySuggestions,
72 | candidateSymbols: candidateSymbols
73 | };
74 | }
75 |
76 | var initialTranslation = translate("");
77 |
78 | function lookup(symbol) {
79 | return Rebase.$$Option.flatMap((function (param) {
80 | return Js_dict.get(rawTable, param);
81 | }), Rebase.$$Option.map((function (prim) {
82 | return String(prim);
83 | }), symbol.codePointAt(0)));
84 | }
85 |
86 | exports.rawTable = rawTable;
87 | exports.rawKeymapObject = rawKeymapObject;
88 | exports.fromObject = fromObject;
89 | exports.keymap = keymap;
90 | exports.toKeySuggestions = toKeySuggestions;
91 | exports.toCandidateSymbols = toCandidateSymbols;
92 | exports.isInKeymap = isInKeymap;
93 | exports.translate = translate;
94 | exports.initialTranslation = initialTranslation;
95 | exports.lookup = lookup;
96 | /* rawTable Not a pure module */
97 |
--------------------------------------------------------------------------------
/src/Instance/Instance__TextEditors.re:
--------------------------------------------------------------------------------
1 | open! Rebase;
2 |
3 | open Instance__Type;
4 |
5 | let getPath = instance => {
6 | instance.editors.source
7 | |> Atom.TextEditor.getPath
8 | |> Option.getOr("untitled")
9 | |> Parser.filepath;
10 | };
11 |
12 | let pointingAt = (~cursor=?, instance): option(Goal.t) => {
13 | let cursor_ =
14 | switch (cursor) {
15 | | None => Atom.TextEditor.getCursorBufferPosition(instance.editors.source)
16 | | Some(x) => x
17 | };
18 |
19 | let pointedGoals =
20 | instance.goals
21 | |> Array.filter(goal =>
22 | Atom.Range.containsPoint(cursor_, goal.Goal.range)
23 | );
24 | // return the first pointed goal
25 | pointedGoals[0];
26 | };
27 |
28 | let getPointedGoal = (instance): Promise.t(result(Goal.t, error)) => {
29 | let pointed = pointingAt(instance);
30 | switch (pointed) {
31 | | Some(goal) => Promise.resolved(Ok(goal))
32 | | None => Promise.resolved(Error(OutOfGoal))
33 | };
34 | };
35 |
36 | // execute the callback
37 | // if it's pointing at some empty hole
38 | // then move the cursor inside the empty hole
39 | // else restore the cursor to its original position
40 | let restoreCursorPosition = (callback, instance) => {
41 | let originalPosition =
42 | Atom.TextEditor.getCursorBufferPosition(instance.editors.source);
43 |
44 | callback()
45 | ->Promise.map(result => {
46 | let pointed = pointingAt(~cursor=originalPosition, instance);
47 | switch (pointed) {
48 | | Some(goal) =>
49 | if (Goal.isEmpty(goal)) {
50 | Instance__Goals.setCursor(goal, instance);
51 | } else {
52 | Atom.TextEditor.setCursorBufferPosition(
53 | originalPosition,
54 | instance.editors.source,
55 | );
56 | }
57 | | None =>
58 | Atom.TextEditor.setCursorBufferPosition(
59 | originalPosition,
60 | instance.editors.source,
61 | )
62 | };
63 | result;
64 | });
65 | };
66 |
67 | //
68 | // History Management
69 | //
70 |
71 | // sometimes a child command may be invoked by some parent command,
72 | // in that case, both the parent and the child command should be
73 | // regarded as a single action
74 |
75 | let startCheckpoint = (command, instance) => {
76 | let checkpoint = instance.editors.source |> Atom.TextEditor.createCheckpoint;
77 | instance.history.checkpoints |> Js.Array.push(checkpoint) |> ignore;
78 |
79 | // see if reloading is needed on undo
80 | if (Array.length(instance.history.checkpoints) === 1) {
81 | instance.history.needsReloading =
82 | Command.(
83 | switch (command) {
84 | | SolveConstraints
85 | | Give
86 | | Refine
87 | | Auto
88 | | Case => true
89 | | _ => false
90 | }
91 | );
92 | };
93 | };
94 |
95 | let endCheckpoint = instance => {
96 | let checkpoint = Js.Array.pop(instance.history.checkpoints);
97 | // group changes if it's a parent command
98 | if (Array.length(instance.history.checkpoints) === 0) {
99 | checkpoint
100 | |> Option.forEach(n =>
101 | instance.editors.source
102 | |> Atom.TextEditor.groupChangesSinceCheckpoint(n)
103 | |> ignore
104 | );
105 | };
106 | ();
107 | };
108 |
--------------------------------------------------------------------------------
/src/Instance/Instance__Connections.re:
--------------------------------------------------------------------------------
1 | open! Rebase;
2 |
3 | open Instance__Type;
4 | open Atom;
5 |
6 | // inquire Agda path from the user
7 | let inquireAgdaPath =
8 | (error: option(Connection.Error.t), instance)
9 | : Promise.t(result(string, error)) => {
10 | View.(
11 | instance.view.activate()
12 | ->Promise.flatMap(() =>
13 | instance.view.navigateSettings(Settings.URI.Connection)
14 | )
15 | ->Promise.flatMap(() => instance.view.updateConnection(None, error))
16 | ->Promise.flatMap(() => instance.view.inquireConnection())
17 | ->Promise.mapError(_ => Instance__Type.Cancelled)
18 | );
19 | };
20 |
21 | // get Agda path from config or from the user
22 | let getAgdaPathAndArgs =
23 | (instance): Promise.t(result((string, array(string)), error)) => {
24 | let storedPath = Config.get("agda-mode.agdaPath") |> Parser.filepath;
25 | let storedArgs = Config.get("agda-mode.agdaArgs") |> Parser.commandLineArgs;
26 | if (String.isEmpty(storedPath) || storedPath == ".") {
27 | let searchingFor = Config.get("agda-mode.agdaName") |> String.trim;
28 | Connection.autoSearch(searchingFor)
29 | ->Promise.flatMapError(err => {
30 | // log connection error for testing
31 | instance.onConnectionError.emit(err);
32 | inquireAgdaPath(Some(err), instance);
33 | })
34 | ->Promise.mapOk(path => (path, [||]));
35 | } else {
36 | Promise.resolved(Ok((storedPath, storedArgs)));
37 | };
38 | };
39 |
40 | let persistConnection =
41 | (instance, connection: Connection.t)
42 | : Promise.t(result(Connection.t, error)) => {
43 | instance.connection = Some(connection);
44 | /* store the path in the config */
45 | let path =
46 | Array.concat(connection.metadata.args, [|connection.metadata.path|])
47 | |> List.fromArray
48 | |> String.joinWith(" ");
49 | Config.set("agda-mode.agdaPath", path) |> ignore;
50 | // update the view, and then pass the connection out
51 | instance.view.updateConnection(Some(connection), None)
52 | ->Promise.map(() => Ok(connection));
53 | };
54 |
55 | let connectWithAgdaPathAndArgs =
56 | (instance, path, args): Promise.t(result(Connection.t, error)) => {
57 | // validate the given path
58 | let rec getMetadata = (instance, path, args) => {
59 | Connection.validateAndMake(path, args)
60 | ->Promise.flatMapError(err =>
61 | inquireAgdaPath(Some(err), instance)
62 | ->Promise.flatMapOk(p => getMetadata(instance, p, args))
63 | );
64 | };
65 |
66 | instance
67 | ->getMetadata(path, args)
68 | ->Promise.mapOk(Connection.connect)
69 | ->Promise.flatMapOk(persistConnection(instance))
70 | ->Promise.mapOk(Connection.wire);
71 | };
72 |
73 | // would stuck (and wait for the user) if the path is wrong, not suitable for testing
74 | let connect = (instance): Promise.t(result(Connection.t, error)) => {
75 | switch (instance.connection) {
76 | | Some(connection) => Promise.resolved(Ok(connection))
77 | | None =>
78 | instance
79 | ->getAgdaPathAndArgs
80 | ->Promise.flatMapOk(((path, args)) =>
81 | connectWithAgdaPathAndArgs(instance, path, args)
82 | )
83 | };
84 | };
85 |
86 | let disconnect = instance => {
87 | switch (instance.connection) {
88 | | Some(connection) =>
89 | Connection.disconnect(Process.Error.DisconnectedByUser, connection);
90 | instance.connection = None;
91 | instance.view.updateConnection(None, None);
92 | | None => Promise.resolved()
93 | };
94 | };
--------------------------------------------------------------------------------
/lib/js/src/Instance.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Curry = require("bs-platform/lib/js/curry.js");
5 | var $$Promise = require("reason-promise/lib/js/src/js/promise.js");
6 | var Root$AgdaMode = require("./View/Root.bs.js");
7 | var Event$AgdaMode = require("./Util/Event.bs.js");
8 | var Editors$AgdaMode = require("./Editors.bs.js");
9 | var TaskRunner$AgdaMode = require("./Task/TaskRunner.bs.js");
10 | var Caml_chrome_debugger = require("bs-platform/lib/js/caml_chrome_debugger.js");
11 | var RunningInfo$AgdaMode = require("./RunningInfo.bs.js");
12 |
13 | function activate(instance) {
14 | if (instance.isLoaded) {
15 | return Curry._1(instance.view.activate, /* () */0);
16 | } else {
17 | return $$Promise.resolved(/* () */0);
18 | }
19 | }
20 |
21 | function deactivate(instance) {
22 | if (instance.isLoaded) {
23 | return Curry._1(instance.view.deactivate, /* () */0);
24 | } else {
25 | return $$Promise.resolved(/* () */0);
26 | }
27 | }
28 |
29 | function destroy(instance) {
30 | return Curry._1(instance.view.destroy, /* () */0);
31 | }
32 |
33 | function make(textEditor) {
34 | atom.views.getView(textEditor).classList.add("agda");
35 | var editors = Editors$AgdaMode.make(textEditor);
36 | var view = Root$AgdaMode.initialize(editors);
37 | var instance = {
38 | editors: editors,
39 | view: view,
40 | history: {
41 | checkpoints: [],
42 | needsReloading: false
43 | },
44 | isLoaded: false,
45 | highlightings: [],
46 | goals: [],
47 | connection: undefined,
48 | runningInfo: RunningInfo$AgdaMode.make(/* () */0),
49 | onDispatch: Event$AgdaMode.make(/* () */0),
50 | onConnectionError: Event$AgdaMode.make(/* () */0)
51 | };
52 | var destructor1 = Curry._1(instance.view.onMouseEvent.on, (function (ev) {
53 | switch (ev.tag | 0) {
54 | case /* JumpToTarget */0 :
55 | TaskRunner$AgdaMode.dispatchCommand(/* Jump */Caml_chrome_debugger.variant("Jump", 9, [ev[0]]), instance);
56 | return /* () */0;
57 | case /* MouseOver */1 :
58 | case /* MouseOut */2 :
59 | return /* () */0;
60 |
61 | }
62 | }));
63 | $$Promise.get(Curry._1(instance.view.onDestroy.once, /* () */0), (function (param) {
64 | return Curry._1(destructor1, /* () */0);
65 | }));
66 | return instance;
67 | }
68 |
69 | function dispatchUndo(instance) {
70 | instance.editors.source.undo();
71 | if (instance.history.needsReloading) {
72 | TaskRunner$AgdaMode.dispatchCommand(/* Load */0, instance);
73 | instance.history.needsReloading = false;
74 | return /* () */0;
75 | } else {
76 | return 0;
77 | }
78 | }
79 |
80 | function getID(instance) {
81 | return Editors$AgdaMode.getID(instance.editors);
82 | }
83 |
84 | var $$Event = /* alias */0;
85 |
86 | var Goals = /* alias */0;
87 |
88 | var Highlightings = /* alias */0;
89 |
90 | var Connections = /* alias */0;
91 |
92 | var TextEditors = /* alias */0;
93 |
94 | exports.$$Event = $$Event;
95 | exports.Goals = Goals;
96 | exports.Highlightings = Highlightings;
97 | exports.Connections = Connections;
98 | exports.TextEditors = TextEditors;
99 | exports.activate = activate;
100 | exports.deactivate = deactivate;
101 | exports.destroy = destroy;
102 | exports.make = make;
103 | exports.dispatchUndo = dispatchUndo;
104 | exports.getID = getID;
105 | /* Promise Not a pure module */
106 |
--------------------------------------------------------------------------------
/lib/js/test/Parser/Test__Parser__SourceFile.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Belt_Array = require("bs-platform/lib/js/belt_Array.js");
5 | var Mocha$BsMocha = require("bs-mocha/lib/js/src/Mocha.bs.js");
6 | var Promise$BsMocha = require("bs-mocha/lib/js/src/Promise.bs.js");
7 | var SourceFile$AgdaMode = require("../../src/SourceFile.bs.js");
8 | var Test__Util$AgdaMode = require("../Test__Util.bs.js");
9 |
10 | Mocha$BsMocha.describe("when parsing file paths")(undefined, undefined, undefined, (function (param) {
11 | return Mocha$BsMocha.it("should recognize the file extensions")(undefined, undefined, undefined, (function (param) {
12 | Test__Util$AgdaMode.Assert.equal(undefined, SourceFile$AgdaMode.FileType.parse("a.agda"), /* Agda */0);
13 | Test__Util$AgdaMode.Assert.equal(undefined, SourceFile$AgdaMode.FileType.parse("a.lagda"), /* LiterateTeX */1);
14 | Test__Util$AgdaMode.Assert.equal(undefined, SourceFile$AgdaMode.FileType.parse("a.lagda.tex"), /* LiterateTeX */1);
15 | Test__Util$AgdaMode.Assert.equal(undefined, SourceFile$AgdaMode.FileType.parse("a.lagda.md"), /* LiterateMarkdown */3);
16 | Test__Util$AgdaMode.Assert.equal(undefined, SourceFile$AgdaMode.FileType.parse("a.lagda.rst"), /* LiterateRST */2);
17 | return Test__Util$AgdaMode.Assert.equal(undefined, SourceFile$AgdaMode.FileType.parse("a.lagda.org"), /* LiterateOrg */4);
18 | }));
19 | }));
20 |
21 | Mocha$BsMocha.describe("when parsing source files")(undefined, undefined, undefined, (function (param) {
22 | return Belt_Array.forEach(Test__Util$AgdaMode.Golden.getGoldenFilepathsSync("test/Parser/SourceFile"), (function (filepath) {
23 | return Promise$BsMocha.it("should golden test " + filepath)(undefined, undefined, undefined, (function (param) {
24 | return Test__Util$AgdaMode.Golden.readFile(filepath).then((function (raw) {
25 | var partial_arg = [
26 | 0,
27 | 1,
28 | 2,
29 | 3,
30 | 4,
31 | 5,
32 | 6,
33 | 7,
34 | 8,
35 | 9
36 | ];
37 | return Test__Util$AgdaMode.Golden.compare(Test__Util$AgdaMode.Golden.map(Test__Util$AgdaMode.Golden.map(raw, (function (param) {
38 | return SourceFile$AgdaMode.parse(partial_arg, filepath, param);
39 | })), (function (param) {
40 | return Test__Util$AgdaMode.serializeWith(SourceFile$AgdaMode.Diff.toString, param);
41 | })));
42 | }));
43 | }));
44 | }));
45 | }));
46 |
47 | /* Not a pure module */
48 |
--------------------------------------------------------------------------------
/lib/js/src/Instance/Instance__TextEditors.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Curry = require("bs-platform/lib/js/curry.js");
5 | var Rebase = require("@glennsl/rebase/lib/js/src/Rebase.bs.js");
6 | var $$Promise = require("reason-promise/lib/js/src/js/promise.js");
7 | var Caml_option = require("bs-platform/lib/js/caml_option.js");
8 | var Goal$AgdaMode = require("../Goal.bs.js");
9 | var Parser$AgdaMode = require("../Parser.bs.js");
10 | var Caml_chrome_debugger = require("bs-platform/lib/js/caml_chrome_debugger.js");
11 | var Instance__Goals$AgdaMode = require("./Instance__Goals.bs.js");
12 |
13 | function getPath(instance) {
14 | return Parser$AgdaMode.filepath(Rebase.$$Option.getOr("untitled", instance.editors.source.getPath()));
15 | }
16 |
17 | function pointingAt(cursor, instance) {
18 | var cursor_ = cursor !== undefined ? Caml_option.valFromOption(cursor) : instance.editors.source.getCursorBufferPosition();
19 | var pointedGoals = Rebase.$$Array.filter((function (goal) {
20 | return goal.range.containsPoint(cursor_);
21 | }), instance.goals);
22 | return Rebase.$$Array.get(pointedGoals, 0);
23 | }
24 |
25 | function getPointedGoal(instance) {
26 | var pointed = pointingAt(undefined, instance);
27 | if (pointed !== undefined) {
28 | return $$Promise.resolved(/* Ok */Caml_chrome_debugger.variant("Ok", 0, [pointed]));
29 | } else {
30 | return $$Promise.resolved(/* Error */Caml_chrome_debugger.variant("Error", 1, [/* OutOfGoal */1]));
31 | }
32 | }
33 |
34 | function restoreCursorPosition(callback, instance) {
35 | var originalPosition = instance.editors.source.getCursorBufferPosition();
36 | return $$Promise.map(Curry._1(callback, /* () */0), (function (result) {
37 | var pointed = pointingAt(Caml_option.some(originalPosition), instance);
38 | if (pointed !== undefined) {
39 | var goal = pointed;
40 | if (Goal$AgdaMode.isEmpty(goal)) {
41 | Instance__Goals$AgdaMode.setCursor(goal, instance);
42 | } else {
43 | instance.editors.source.setCursorBufferPosition(originalPosition);
44 | }
45 | } else {
46 | instance.editors.source.setCursorBufferPosition(originalPosition);
47 | }
48 | return result;
49 | }));
50 | }
51 |
52 | function startCheckpoint(command, instance) {
53 | var checkpoint = instance.editors.source.createCheckpoint();
54 | instance.history.checkpoints.push(checkpoint);
55 | if (Rebase.$$Array.length(instance.history.checkpoints) === 1) {
56 | instance.history.needsReloading = typeof command === "number" ? (
57 | command >= 13 ? command < 17 : command === 6
58 | ) : false;
59 | return /* () */0;
60 | } else {
61 | return 0;
62 | }
63 | }
64 |
65 | function endCheckpoint(instance) {
66 | var checkpoint = instance.history.checkpoints.pop();
67 | if (Rebase.$$Array.length(instance.history.checkpoints) === 0) {
68 | Rebase.$$Option.forEach((function (n) {
69 | instance.editors.source.groupChangesSinceCheckpoint(n);
70 | return /* () */0;
71 | }), checkpoint === undefined ? undefined : Caml_option.some(checkpoint));
72 | }
73 | return /* () */0;
74 | }
75 |
76 | exports.getPath = getPath;
77 | exports.pointingAt = pointingAt;
78 | exports.getPointedGoal = getPointedGoal;
79 | exports.restoreCursorPosition = restoreCursorPosition;
80 | exports.startCheckpoint = startCheckpoint;
81 | exports.endCheckpoint = endCheckpoint;
82 | /* Promise Not a pure module */
83 |
--------------------------------------------------------------------------------
/src/View/Emacs/Emacs__Parser.re:
--------------------------------------------------------------------------------
1 | open Rebase;
2 |
3 | open Rebase.Option;
4 |
5 | /* open Type.View.Emacs; */
6 |
7 | let unindent: array(string) => array(string) =
8 | lines => {
9 | let isNewline = (line, nextLine) => {
10 | let sort = [%re "/^Sort \\S*/"];
11 | let delimeter = [%re "/^\\u2014{4}/g"];
12 | /* banana : Banana */
13 | let completeJudgement = [%re
14 | "/^(?:(?:[^\\(\\{\\s]+\\s+\\:=?)|Have\\:|Goal\\:)\\s* \\S*/"
15 | ];
16 | /* case when the term's name is too long, the rest of the judgement
17 | would go to the next line, e.g:
18 | banananananananananananananananana
19 | : Banana
20 | */
21 | let reallyLongTermIdentifier = [%re "/^\\S+$/"];
22 | let restOfTheJudgement = [%re "/^\\s*\\:=?\\s* \\S*/"];
23 | Js.Re.test_(sort, line)
24 | || Js.Re.test_(delimeter, line)
25 | || Js.Re.test_(reallyLongTermIdentifier, line)
26 | && nextLine
27 | |> exists(line => Js.Re.test_(restOfTheJudgement, line))
28 | || Js.Re.test_(completeJudgement, line);
29 | };
30 | let newLineIndices: array(int) =
31 | lines
32 | |> Array.mapi((line, index) => (line, lines[index + 1], index))
33 | |> Array.filter(((line, nextLine, _)) => isNewline(line, nextLine))
34 | |> Array.map(((_, _, index)) => index);
35 | newLineIndices
36 | |> Array.mapi((index, i) =>
37 | switch (newLineIndices[i + 1]) {
38 | | None => (index, Array.length(lines) + 1)
39 | | Some(n) => (index, n)
40 | }
41 | )
42 | |> Array.map(((from, to_)) =>
43 | lines
44 | |> Array.slice(~from, ~to_)
45 | |> List.fromArray
46 | |> String.joinWith("\n")
47 | );
48 | };
49 |
50 | let partiteMetas =
51 | Util.Dict.split("metas", (rawMetas: array(string)) => {
52 | let metas = unindent(rawMetas);
53 | let indexOfHiddenMetas =
54 | metas
55 | |> Array.findIndex(s =>
56 | s |> Emacs__Component.Output.parseOutputWithRange |> isSome
57 | )
58 | |> map(fst);
59 | metas
60 | |> Util.Dict.partite(((_, i)) =>
61 | switch (indexOfHiddenMetas) {
62 | | Some(n) =>
63 | if (i === n) {
64 | Some("hiddenMetas");
65 | } else if (i === 0) {
66 | Some("interactionMetas");
67 | } else {
68 | None;
69 | }
70 | | None =>
71 | /* All interaction metas */
72 | if (i === 0) {
73 | Some("interactionMetas");
74 | } else {
75 | None;
76 | }
77 | }
78 | );
79 | });
80 |
81 | let partiteWarningsOrErrors = key =>
82 | Util.Dict.update(
83 | key,
84 | (raw: array(string)) => {
85 | let hasDelimeter =
86 | raw[0] |> flatMap(Js.String.match([%re "/^\\u2014{4}/"])) |> isSome;
87 | let lines = hasDelimeter ? raw |> Js.Array.sliceFrom(1) : raw;
88 | let markWarningStart = line =>
89 | line |> Type.Location.Range.parse |> isSome;
90 | /* If the previous warning of error ends with "at", then we have to glue it back */
91 | let glueBack = xs =>
92 | xs[Array.length(xs) - 1]
93 | |> flatMap(Js.String.match([%re "/at$/"]))
94 | |> isSome;
95 | lines
96 | |> Util.Array_.partite(markWarningStart)
97 | |> Util.Array_.mergeWithNext(glueBack)
98 | |> Array.map(xs => xs |> List.fromArray |> String.joinWith("\n"));
99 | },
100 | );
101 |
--------------------------------------------------------------------------------
/src/Instance/Instance__Goals.re:
--------------------------------------------------------------------------------
1 | open Rebase;
2 | open Instance__Type;
3 |
4 | // destroy all goals
5 | let destroyAll = instance => {
6 | instance.goals |> Array.forEach(Goal.destroy);
7 | instance.goals = [||];
8 | };
9 | let find = (index: int, instance) => {
10 | let found = instance.goals |> Array.filter(goal => goal.Goal.index == index);
11 | found[0];
12 | };
13 |
14 | // set the cursor inside the goal
15 | let setCursor = (goal, instance) => {
16 | let position =
17 | Atom.Point.translate(
18 | Atom.Point.make(0, 3),
19 | Atom.Range.start(goal.Goal.range),
20 | );
21 | Atom.TextEditor.setCursorBufferPosition(position, instance.editors.source);
22 | };
23 |
24 | let getPositions = (instance): array(Atom.Point.t) => {
25 | instance.goals
26 | |> Array.map(goal => goal.Goal.range)
27 | |> Array.map(range =>
28 | Atom.Point.translate(Atom.Point.make(0, 3), Atom.Range.start(range))
29 | );
30 | };
31 |
32 | let getPreviousGoalPosition = (instance): option(Atom.Point.t) => {
33 | let previousGoal = ref(None);
34 | let cursor =
35 | Atom.TextEditor.getCursorBufferPosition(instance.editors.source);
36 | let positions = getPositions(instance);
37 |
38 | /* assign the previous goal position */
39 | positions
40 | |> Array.forEach(position =>
41 | if (Atom.Point.isLessThan(cursor, position)) {
42 | previousGoal := Some(position);
43 | }
44 | );
45 |
46 | /* loop back if this is already the first goal */
47 | if (previousGoal^ === None) {
48 | previousGoal := positions[Array.length(positions) - 1];
49 | };
50 |
51 | previousGoal^;
52 | };
53 |
54 | let getNextGoalPosition = (instance): option(Atom.Point.t) => {
55 | let nextGoal = ref(None);
56 | let cursor =
57 | Atom.TextEditor.getCursorBufferPosition(instance.editors.source);
58 | let positions = getPositions(instance);
59 | /* assign the next goal position */
60 | positions
61 | |> Array.forEach(position =>
62 | if (Atom.Point.isGreaterThan(cursor, position) && nextGoal^ === None) {
63 | nextGoal := Some(position);
64 | }
65 | );
66 |
67 | /* if no goal ahead of cursor, then loop back */
68 | if (nextGoal^ === None) {
69 | nextGoal := positions[0];
70 | };
71 |
72 | nextGoal^;
73 | };
74 |
75 | // instantiate all goals
76 | let instantiateAll = (indices, instance) => {
77 | open Atom;
78 | destroyAll(instance);
79 |
80 | let textEditor = instance.editors.source;
81 | let filePath =
82 | textEditor
83 | |> Atom.TextEditor.getPath
84 | |> Option.getOr("untitled")
85 | |> Parser.filepath;
86 | let textBuffer = TextEditor.getBuffer(textEditor);
87 |
88 | let source = TextEditor.getText(textEditor);
89 | instance.goals =
90 | SourceFile.parse(indices, filePath, source)
91 | |> Array.map((result: SourceFile.Diff.t) => {
92 | let start =
93 | TextBuffer.positionForCharacterIndex(
94 | fst(result.originalRange),
95 | textBuffer,
96 | );
97 | let end_ =
98 | TextBuffer.positionForCharacterIndex(
99 | snd(result.originalRange),
100 | textBuffer,
101 | );
102 | let range = Range.make(start, end_);
103 | /* modified the hole */
104 | textEditor
105 | |> TextEditor.setTextInBufferRange(range, result.content)
106 | |> ignore;
107 | /* make it a goal */
108 | Goal.make(
109 | instance.editors.source,
110 | result.index,
111 | result.modifiedRange,
112 | );
113 | });
114 | ();
115 | };
--------------------------------------------------------------------------------
/src/View/Settings/Settings__Connection.re:
--------------------------------------------------------------------------------
1 | open ReasonReact;
2 | open! Rebase;
3 | open Util.React;
4 |
5 | [@react.component]
6 | let make =
7 | (
8 | ~connection: option(Connection.t),
9 | ~error: option(Connection.Error.t),
10 | ~hidden,
11 | ) => {
12 | let channels = React.useContext(Channels.context);
13 | let (autoSearchError, setAutoSearchError) = Hook.useState(None);
14 | let editorRef = Resource.make();
15 | let onSetPath = Event.make();
16 |
17 | /* triggering autoSearch */
18 | let handleAutoSearch = _ => {
19 | let promise = Connection.autoSearch("agda");
20 | promise->Promise.getOk(path =>
21 | editorRef.acquire()
22 | ->Promise.getOk(editor => editor |> Atom.TextEditor.setText(path))
23 | );
24 | // report error when autoSearch failed
25 | promise->Promise.getError(err => setAutoSearchError(Some(err)));
26 | };
27 |
28 | let focusOnPathEditor = editor => {
29 | editor |> Atom.Views.getView |> Webapi.Dom.HtmlElement.focus;
30 | editor |> Atom.TextEditor.selectAll |> ignore;
31 | onSetPath.once();
32 | };
33 |
34 | // focus on the path editor on `inquireConnection`
35 | Hook.useChannel(
36 | () => editorRef.acquire()->Promise.flatMapOk(focusOnPathEditor),
37 | channels.inquireConnection,
38 | );
39 |
40 | let connected = connection |> Option.isSome;
41 | let className =
42 | "agda-settings-connection"
43 | ++ showWhen(!hidden)
44 | ++ when_(!connected, "inquiring");
45 | let status =
46 | connected
47 | ?
52 | : ;
57 |
58 |
59 |
60 | {string("Connection to Agda")}
61 | status
62 |
63 |
64 | {string("Status")}
65 | {switch (connection) {
66 | | None => <> {string("connection not established")}
>
67 | | Some(conn) =>
68 | <>
69 | {string("Path: " ++ conn.metadata.path)}
70 | {string("Version: " ++ conn.metadata.version)}
71 |
72 | {string(
73 | "Supported protocol: "
74 | ++ Connection.Metadata.Protocol.toString(conn.metadata.protocol),
75 | )}
76 |
77 | >
78 | }}
79 |
80 | {string("Path")}
81 |
82 | ""
87 | | Some(conn) => conn.metadata.path
88 | }
89 | }
90 | placeholder="path to Agda"
91 | onEditorRef={x => editorRef.supply(Ok(x))}
92 | onConfirm={path => {
93 | setAutoSearchError(None);
94 | onSetPath.emit(Ok(path));
95 | }}
96 | />
97 |
98 |
99 |
104 |
105 | {switch (autoSearchError) {
106 | | None =>
107 | switch (error) {
108 | | None => null
109 | | Some(err) =>
110 | }
111 | | Some(err) =>
112 | }}
113 | ;
114 | };
--------------------------------------------------------------------------------
/lib/js/src/View/Panel/InputMethod/Buffer.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Rebase = require("@glennsl/rebase/lib/js/src/Rebase.bs.js");
5 | var Translator$AgdaMode = require("./Translator.bs.js");
6 | var Caml_chrome_debugger = require("bs-platform/lib/js/caml_chrome_debugger.js");
7 |
8 | function init(string) {
9 | return string.substring(0, Rebase.$$String.length(string) - 1 | 0);
10 | }
11 |
12 | function isEmpty(self) {
13 | if (self.symbol === undefined) {
14 | return Rebase.$$String.isEmpty(self.tail);
15 | } else {
16 | return false;
17 | }
18 | }
19 |
20 | function toSequence(self) {
21 | var match = self.symbol;
22 | if (match !== undefined) {
23 | return match[1] + self.tail;
24 | } else {
25 | return self.tail;
26 | }
27 | }
28 |
29 | function toSurface(self) {
30 | var match = self.symbol;
31 | if (match !== undefined) {
32 | return match[0] + self.tail;
33 | } else {
34 | return self.tail;
35 | }
36 | }
37 |
38 | function toString(self) {
39 | return "\"" + (toSurface(self) + ("\"[" + (toSequence(self) + "]")));
40 | }
41 |
42 | function next(self, reality) {
43 | var surface = toSurface(self);
44 | var sequence = toSequence(self);
45 | if (reality === surface) {
46 | if (Translator$AgdaMode.translate(sequence).further && reality !== "\\") {
47 | return /* Noop */0;
48 | } else {
49 | return /* Complete */1;
50 | }
51 | } else if (init(reality) === surface) {
52 | var insertedChar = reality.substr(-1);
53 | var sequence$prime = sequence + insertedChar;
54 | var translation = Translator$AgdaMode.translate(sequence$prime);
55 | var match = translation.symbol;
56 | if (match !== undefined) {
57 | var symbol = match;
58 | if (insertedChar === symbol && insertedChar === "\\") {
59 | return /* Stuck */Caml_chrome_debugger.variant("Stuck", 3, [0]);
60 | } else {
61 | return /* Rewrite */Caml_chrome_debugger.variant("Rewrite", 2, [{
62 | symbol: /* tuple */[
63 | symbol,
64 | sequence$prime
65 | ],
66 | tail: ""
67 | }]);
68 | }
69 | } else if (translation.further) {
70 | return /* Insert */Caml_chrome_debugger.variant("Insert", 0, [{
71 | symbol: self.symbol,
72 | tail: self.tail + insertedChar
73 | }]);
74 | } else {
75 | return /* Stuck */Caml_chrome_debugger.variant("Stuck", 3, [1]);
76 | }
77 | } else if (reality === init(surface) || reality === init(sequence)) {
78 | if (Rebase.$$String.isEmpty(reality)) {
79 | if (Rebase.$$Option.isSome(self.symbol)) {
80 | return /* Rewrite */Caml_chrome_debugger.variant("Rewrite", 2, [{
81 | symbol: undefined,
82 | tail: init(sequence)
83 | }]);
84 | } else {
85 | return /* Stuck */Caml_chrome_debugger.variant("Stuck", 3, [2]);
86 | }
87 | } else {
88 | return /* Backspace */Caml_chrome_debugger.variant("Backspace", 1, [{
89 | symbol: self.symbol,
90 | tail: init(self.tail)
91 | }]);
92 | }
93 | } else {
94 | return /* Stuck */Caml_chrome_debugger.variant("Stuck", 3, [3]);
95 | }
96 | }
97 |
98 | var initial = {
99 | symbol: undefined,
100 | tail: ""
101 | };
102 |
103 | exports.init = init;
104 | exports.initial = initial;
105 | exports.isEmpty = isEmpty;
106 | exports.toSequence = toSequence;
107 | exports.toSurface = toSurface;
108 | exports.toString = toString;
109 | exports.next = next;
110 | /* Translator-AgdaMode Not a pure module */
111 |
--------------------------------------------------------------------------------
/src/View/Panel/Dashboard.re:
--------------------------------------------------------------------------------
1 | open ReasonReact;
2 | open Util.React;
3 | open Rebase;
4 |
5 | [@react.component]
6 | let make =
7 | (
8 | ~header: Type.View.Header.t,
9 | ~hidden: bool,
10 | ~isPending: bool,
11 | ~mountingPoint: Type.View.mountingPoint,
12 | ~settingsActivated: bool,
13 | ~onMountingTargetChange: Type.View.mountingTarget => unit,
14 | ~onSettingsViewToggle: bool => unit,
15 | ) => {
16 | let settingsButtonRef = React.useRef(Js.Nullable.null);
17 | let dockingButtonRef = React.useRef(Js.Nullable.null);
18 |
19 | React.useEffect1(
20 | () =>
21 | settingsButtonRef
22 | |> React.Ref.current
23 | |> Js.Nullable.toOption
24 | |> Option.flatMap(settingsButton => {
25 | let disposable =
26 | Atom.Tooltips.add(
27 | Atom.Views.getView(settingsButton),
28 | {
29 | "title": "settings",
30 | "delay": {
31 | "show": 100,
32 | "hide": 1000,
33 | },
34 | },
35 | );
36 | Some(() => disposable |> Atom.Disposable.dispose);
37 | }),
38 | [||],
39 | );
40 |
41 | React.useEffect1(
42 | () =>
43 | dockingButtonRef
44 | |> React.Ref.current
45 | |> Js.Nullable.toOption
46 | |> Option.flatMap(dockingButton => {
47 | let disposable =
48 | Atom.Tooltips.add(
49 | Atom.Views.getView(dockingButton),
50 | {
51 | "title": "toggle panel docking position",
52 | "delay": {
53 | "show": 300,
54 | "hide": 1000,
55 | },
56 | "keyBindingCommand": "agda-mode:toggle-docking",
57 | },
58 | );
59 | Some(() => disposable |> Atom.Disposable.dispose);
60 | }),
61 | [||],
62 | );
63 |
64 | let headerClassList =
65 | switch (header.style) {
66 | | PlainText => ""
67 | | Error => "text-error"
68 | | Info => "text-info"
69 | | Success => "text-success"
70 | | Warning => "text-warning"
71 | };
72 | let spinnerClassList =
73 | "loading loading-spinner-tiny inline-block" ++ when_(isPending, "pending");
74 | let settingsViewClassList =
75 | "no-btn" ++ when_(settingsActivated, "activated");
76 | let toggleMountingPosition =
77 | "no-btn"
78 | ++ when_(
79 | switch (mountingPoint) {
80 | | Pane(_) => true
81 | | _ => false
82 | },
83 | "activated",
84 | );
85 |
86 |
87 |
{string(header.text)}
88 |
89 | -
90 | -
91 |
98 |
99 | -
100 |
111 |
112 |
113 |
;
114 | };
115 |
--------------------------------------------------------------------------------
/lib/js/src/View/View.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Curry = require("bs-platform/lib/js/curry.js");
5 | var $$Promise = require("reason-promise/lib/js/src/js/promise.js");
6 | var Event$AgdaMode = require("../Util/Event.bs.js");
7 | var Channel$AgdaMode = require("../Util/Channel.bs.js");
8 |
9 | function make(events, channels) {
10 | var activate = function (param) {
11 | return Channel$AgdaMode.send(/* () */0, channels.activatePanel);
12 | };
13 | var deactivate = function (param) {
14 | return Channel$AgdaMode.send(/* () */0, channels.deactivatePanel);
15 | };
16 | var toggleDocking = function (param) {
17 | return Channel$AgdaMode.send(/* () */0, channels.toggleDocking);
18 | };
19 | var display = function (text, style, body) {
20 | return Channel$AgdaMode.send(/* tuple */[
21 | {
22 | text: text,
23 | style: style
24 | },
25 | body
26 | ], channels.display);
27 | };
28 | var inquire = function (text, placeholder, value) {
29 | return $$Promise.flatMap(Channel$AgdaMode.send(/* () */0, channels.activatePanel), (function (param) {
30 | return Channel$AgdaMode.send(/* tuple */[
31 | {
32 | text: text,
33 | style: /* PlainText */0
34 | },
35 | placeholder,
36 | value
37 | ], channels.inquire);
38 | }));
39 | };
40 | var updateIsPending = function (isPending) {
41 | return Channel$AgdaMode.send(isPending, channels.updateIsPending);
42 | };
43 | var onMouseEvent = events.onMouseEvent;
44 | var activateInputMethod = function (activate) {
45 | return Channel$AgdaMode.send(activate, channels.activateInputMethod);
46 | };
47 | var interceptAndInsertKey = function (symbol) {
48 | return Channel$AgdaMode.send(symbol, channels.interceptAndInsertKey);
49 | };
50 | var onInputMethodChange = events.onInputMethodChange;
51 | var navigateSettings = function (where) {
52 | return Channel$AgdaMode.send(where, channels.navigateSettings);
53 | };
54 | var updateConnection = function (connection, error) {
55 | return Channel$AgdaMode.send(/* tuple */[
56 | connection,
57 | error
58 | ], channels.updateConnection);
59 | };
60 | var inquireConnection = function (param) {
61 | return Channel$AgdaMode.send(/* () */0, channels.inquireConnection);
62 | };
63 | var onDestroy = Event$AgdaMode.make(/* () */0);
64 | var destroy = function (param) {
65 | return $$Promise.tap($$Promise.flatMap($$Promise.flatMap(Channel$AgdaMode.send(/* () */0, channels.deactivatePanel), (function (param) {
66 | return Channel$AgdaMode.send(false, channels.activateInputMethod);
67 | })), (function (param) {
68 | return Channel$AgdaMode.send(/* () */0, channels.destroy);
69 | })), (function (param) {
70 | return Curry._1(onDestroy.emit, /* () */0);
71 | }));
72 | };
73 | return {
74 | activate: activate,
75 | deactivate: deactivate,
76 | toggleDocking: toggleDocking,
77 | display: display,
78 | inquire: inquire,
79 | updateIsPending: updateIsPending,
80 | destroy: destroy,
81 | onDestroy: onDestroy,
82 | onMouseEvent: onMouseEvent,
83 | activateInputMethod: activateInputMethod,
84 | interceptAndInsertKey: interceptAndInsertKey,
85 | onInputMethodChange: onInputMethodChange,
86 | navigateSettings: navigateSettings,
87 | updateConnection: updateConnection,
88 | inquireConnection: inquireConnection
89 | };
90 | }
91 |
92 | exports.make = make;
93 | /* Promise Not a pure module */
94 |
--------------------------------------------------------------------------------
/src/View/Root.re:
--------------------------------------------------------------------------------
1 | open Rebase;
2 |
3 | open Type.View;
4 |
5 | module Event = Event;
6 |
7 | [@react.component]
8 | let make = (~editors: Editors.t, ~events: Events.t, ~channels: Channels.t) => {
9 | ////////////////////////////////////////////
10 | //
11 | ////////////////////////////////////////////
12 | let (settingsURI, setSettingsURI) = Hook.useState(Settings.URI.Root);
13 | let settingsViewRef = React.useRef(None);
14 |
15 | let (settingsElement, setSettingsElement) = Hook.useState(None);
16 |
17 | let updateSettings =
18 | fun
19 | | None => {
20 | // update settingsViewRef
21 | React.Ref.setCurrent(settingsViewRef, None);
22 | // update settingsElement
23 | setSettingsElement(None);
24 | // update settingsURI
25 | setSettingsURI(Settings.URI.Root);
26 | }
27 | | Some((uri, tab)) => {
28 | // update settingsViewRef
29 | React.Ref.setCurrent(settingsViewRef, Some(tab));
30 | // update settingsElement
31 | settingsViewRef
32 | |> React.Ref.current
33 | |> Option.map(Tab.getElement)
34 | |> setSettingsElement;
35 | // update settingsURI
36 | setSettingsURI(uri);
37 | };
38 |
39 | Hook.useChannel(
40 | fun
41 | | None =>
42 | switch (settingsViewRef |> React.Ref.current) {
43 | // Close => Close
44 | | None => Promise.resolved()
45 | // Open => Close
46 | | Some(tab) =>
47 | Tab.kill(tab);
48 | updateSettings(None);
49 | Promise.resolved();
50 | }
51 | | Some(address) =>
52 | switch (settingsViewRef |> React.Ref.current) {
53 | // Close => Open
54 | | None =>
55 | let resource = Resource.make();
56 | let tab =
57 | Tab.make(
58 | ~editor=editors.source,
59 | ~getTitle=
60 | () => "[Settings] " ++ Atom.TextEditor.getTitle(editors.source),
61 | ~path="settings",
62 | ~onOpen=(_, _, _) => resource.supply(),
63 | ~onClose=_ => updateSettings(None),
64 | (),
65 | );
66 | updateSettings(Some((address, tab)));
67 | resource.acquire();
68 | // Open => Open
69 | | Some(_) => Promise.resolved()
70 | },
71 | channels.navigateSettings,
72 | );
73 |
74 | let (debug, debugDispatch) =
75 | React.useReducer(Debug.reducer, Debug.initialState);
76 |
77 | let settingsActivated =
78 | switch (settingsViewRef |> React.Ref.current) {
79 | | Some(_) => true
80 | | None => false
81 | };
82 |
83 | let onSettingsViewToggle = shouldOpen =>
84 | channels.navigateSettings
85 | |> Channel.send(shouldOpen ? Some(Settings__Breadcrumb.Root) : None)
86 | |> ignore;
87 |
88 | let {Events.onInputMethodChange} = events;
89 | <>
90 |
91 | events.onMouseEvent.emit(event)}>
92 |
93 |
100 |
101 |
102 |
103 |
104 | >;
105 | };
106 |
107 | let initialize = editors => {
108 | open Webapi.Dom;
109 | let element = document |> Document.createElement("article");
110 | let events = Events.make();
111 | let channels = Channels.make();
112 | let view = View.make(events, channels);
113 |
114 | let component =
115 | React.createElementVariadic(
116 | make,
117 | makeProps(~editors, ~events, ~channels, ()),
118 | [||],
119 | );
120 |
121 | ReactDOMRe.render(component, element);
122 | /* return the handles for drilling */
123 | view;
124 | };
--------------------------------------------------------------------------------
/src/View/Settings/Settings__Log.re:
--------------------------------------------------------------------------------
1 | open ReasonReact;
2 | open Util.React;
3 | open Rebase;
4 |
5 | module Entry = {
6 | [@react.component]
7 | let make = (~entry: Log.Entry.t) => {
8 | let (hidden, setHidden) = Hook.useState(true);
9 | let className = hidden ? "hidden" : "";
10 | let rawTexts =
11 | entry.response.rawText
12 | |> Array.mapi((text, i) =>
13 | {string(text)}
14 | )
15 | |> React.array;
16 | let sexpressions =
17 | entry.response.sexpression
18 | |> Array.mapi((text, i) =>
19 |
20 | {string(Parser.SExpression.toString(text))}
21 |
22 | )
23 | |> React.array;
24 | let responses =
25 | entry.response.response
26 | |> Array.mapi((text, i) =>
27 |
28 | {string(Response.toString(text))}
29 |
30 | )
31 | |> React.array;
32 | let hasError = Array.length(entry.response.error) > 0;
33 | let errors =
34 | entry.response.error
35 | |> Array.mapi((text, i) =>
36 |
37 | {string(Parser.Error.toString(text))}
38 |
39 | )
40 | |> React.array;
41 |
42 |
43 | setHidden(!hidden)}>
44 | {string(Request.toString(entry.request))}
45 |
46 |
47 | {string("raw text")}
48 | rawTexts
49 |
50 | {string("s-expression")}
51 | sexpressions
52 |
53 | {string("response")}
54 | responses
55 | {hasError
56 | ? <>
{string("error")}
errors
>
57 | : null}
58 |
59 | ;
60 | };
61 | };
62 |
63 | [@react.component]
64 | let make = (~connection: option(Connection.t), ~hidden) => {
65 | let (showInstruction, setShowInstruction) = Hook.useState(false);
66 | let (refreshOnLoad, setRefreshOnLoad) = Hook.useState(true);
67 | connection
68 | |> Option.forEach(conn => conn.Connection.resetLogOnLoad = refreshOnLoad);
69 |
70 | let entries =
71 | connection
72 | |> Option.mapOr(conn => conn.Connection.log, [||])
73 | |> Array.mapi((entry, i) => )
74 | |> React.array;
75 |
76 |
77 |
78 |
79 | {string("Log")}
80 |
81 |
82 |
83 | {string(
84 | "Keeps track of what Agda said what we've parsed. For reporting parse errors. ",
85 | )}
86 |
87 |
88 |
97 |
98 |
99 |
107 |
108 | {showInstruction
109 | ?
110 | {string(
111 | "In case of parse error, please copy the log and paste it ",
112 | )}
113 |
114 | {string("here")}
115 |
116 |
117 | : null}
118 |
119 | entries
120 | ;
121 | };
--------------------------------------------------------------------------------
/lib/js/src/Instance/Instance__Goals.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Atom = require("atom");
5 | var Rebase = require("@glennsl/rebase/lib/js/src/Rebase.bs.js");
6 | var Caml_option = require("bs-platform/lib/js/caml_option.js");
7 | var Goal$AgdaMode = require("../Goal.bs.js");
8 | var Parser$AgdaMode = require("../Parser.bs.js");
9 | var SourceFile$AgdaMode = require("../SourceFile.bs.js");
10 |
11 | function destroyAll(instance) {
12 | Rebase.$$Array.forEach(Goal$AgdaMode.destroy, instance.goals);
13 | instance.goals = [];
14 | return /* () */0;
15 | }
16 |
17 | function find(index, instance) {
18 | var found = Rebase.$$Array.filter((function (goal) {
19 | return goal.index === index;
20 | }), instance.goals);
21 | return Rebase.$$Array.get(found, 0);
22 | }
23 |
24 | function setCursor(goal, instance) {
25 | var position = goal.range.start.translate(new Atom.Point(0, 3));
26 | instance.editors.source.setCursorBufferPosition(position);
27 | return /* () */0;
28 | }
29 |
30 | function getPositions(instance) {
31 | return Rebase.$$Array.map((function (range) {
32 | return range.start.translate(new Atom.Point(0, 3));
33 | }), Rebase.$$Array.map((function (goal) {
34 | return goal.range;
35 | }), instance.goals));
36 | }
37 |
38 | function getPreviousGoalPosition(instance) {
39 | var previousGoal = {
40 | contents: undefined
41 | };
42 | var cursor = instance.editors.source.getCursorBufferPosition();
43 | var positions = getPositions(instance);
44 | Rebase.$$Array.forEach((function (position) {
45 | if (position.isLessThan(cursor)) {
46 | previousGoal.contents = Caml_option.some(position);
47 | return /* () */0;
48 | } else {
49 | return 0;
50 | }
51 | }), positions);
52 | if (previousGoal.contents === undefined) {
53 | previousGoal.contents = Rebase.$$Array.get(positions, Rebase.$$Array.length(positions) - 1 | 0);
54 | }
55 | return previousGoal.contents;
56 | }
57 |
58 | function getNextGoalPosition(instance) {
59 | var nextGoal = {
60 | contents: undefined
61 | };
62 | var cursor = instance.editors.source.getCursorBufferPosition();
63 | var positions = getPositions(instance);
64 | Rebase.$$Array.forEach((function (position) {
65 | if (position.isGreaterThan(cursor) && nextGoal.contents === undefined) {
66 | nextGoal.contents = Caml_option.some(position);
67 | return /* () */0;
68 | } else {
69 | return 0;
70 | }
71 | }), positions);
72 | if (nextGoal.contents === undefined) {
73 | nextGoal.contents = Rebase.$$Array.get(positions, 0);
74 | }
75 | return nextGoal.contents;
76 | }
77 |
78 | function instantiateAll(indices, instance) {
79 | destroyAll(instance);
80 | var textEditor = instance.editors.source;
81 | var filePath = Parser$AgdaMode.filepath(Rebase.$$Option.getOr("untitled", textEditor.getPath()));
82 | var textBuffer = textEditor.getBuffer();
83 | var source = textEditor.getText();
84 | instance.goals = Rebase.$$Array.map((function (result) {
85 | var start = textBuffer.positionForCharacterIndex(result.originalRange[0]);
86 | var end_ = textBuffer.positionForCharacterIndex(result.originalRange[1]);
87 | var range = new Atom.Range(start, end_);
88 | textEditor.setTextInBufferRange(range, result.content);
89 | return Goal$AgdaMode.make(instance.editors.source, result.index, result.modifiedRange);
90 | }), SourceFile$AgdaMode.parse(indices, filePath, source));
91 | return /* () */0;
92 | }
93 |
94 | exports.destroyAll = destroyAll;
95 | exports.find = find;
96 | exports.setCursor = setCursor;
97 | exports.getPositions = getPositions;
98 | exports.getPreviousGoalPosition = getPreviousGoalPosition;
99 | exports.getNextGoalPosition = getNextGoalPosition;
100 | exports.instantiateAll = instantiateAll;
101 | /* atom Not a pure module */
102 |
--------------------------------------------------------------------------------
/lib/js/src/Instance/Instance__Highlightings.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Fs = require("fs");
5 | var Atom = require("atom");
6 | var Util = require("util");
7 | var Rebase = require("@glennsl/rebase/lib/js/src/Rebase.bs.js");
8 | var $$Promise = require("reason-promise/lib/js/src/js/promise.js");
9 | var Parser$AgdaMode = require("../Parser.bs.js");
10 | var Caml_chrome_debugger = require("bs-platform/lib/js/caml_chrome_debugger.js");
11 | var Highlighting$AgdaMode = require("../Highlighting.bs.js");
12 |
13 | function add(annotation, instance) {
14 | var textBuffer = instance.editors.source.getBuffer();
15 | var startPoint = textBuffer.positionForCharacterIndex(annotation.start - 1 | 0);
16 | var endPoint = textBuffer.positionForCharacterIndex(annotation.end_ - 1 | 0);
17 | var range = new Atom.Range(startPoint, endPoint);
18 | var marker = instance.editors.source.markBufferRange(range);
19 | instance.highlightings.push(marker);
20 | var types = annotation.types.join(" ");
21 | instance.editors.source.decorateMarker(marker, {
22 | type: "highlight",
23 | class: "highlight-decoration " + types
24 | });
25 | return /* () */0;
26 | }
27 |
28 | function addMany(annotations, instance) {
29 | return Rebase.$$Array.forEach((function (annotation) {
30 | return add(annotation, instance);
31 | }), Rebase.$$Array.filter(Highlighting$AgdaMode.Annotation.shouldHighlight, annotations));
32 | }
33 |
34 | function addFromFile(filepath, instance) {
35 | var readFile = Util.promisify((function (prim, prim$1) {
36 | Fs.readFile(prim, prim$1);
37 | return /* () */0;
38 | }));
39 | return $$Promise.map($$Promise.Js.toResult($$Promise.Js.fromBsPromise(readFile(filepath))), (function (param) {
40 | if (param.tag) {
41 | console.log(param[0]);
42 | console.log("cannot read the indirect highlighting file: " + filepath);
43 | return /* () */0;
44 | } else {
45 | Rebase.$$Array.forEach((function (annotation) {
46 | return add(annotation, instance);
47 | }), Rebase.$$Array.filter(Highlighting$AgdaMode.Annotation.shouldHighlight, Rebase.$$Array.flatMap((function (x) {
48 | return x;
49 | }), Rebase.$$Array.map((function (tokens) {
50 | if (tokens.tag) {
51 | return Highlighting$AgdaMode.Annotation.parseIndirectHighlightings(tokens[0]);
52 | } else {
53 | return [];
54 | }
55 | }), Rebase.$$Array.filterMap(Rebase.$$Option.fromResult, Parser$AgdaMode.SExpression.parse(param[0].toString()))))));
56 | return /* () */0;
57 | }
58 | }));
59 | }
60 |
61 | function destroyAll(instance) {
62 | Rebase.$$Array.forEach((function (prim) {
63 | prim.destroy();
64 | return /* () */0;
65 | }), instance.highlightings);
66 | instance.highlightings = [];
67 | return /* () */0;
68 | }
69 |
70 | function execute(instance, param) {
71 | if (param.tag) {
72 | var filepath = param[0];
73 | return $$Promise.map($$Promise.map(addFromFile(filepath, instance), (function (param) {
74 | return /* Ok */Caml_chrome_debugger.variant("Ok", 0, [(Fs.unlink(filepath, (function (param) {
75 | return /* () */0;
76 | })), /* () */0)]);
77 | })), (function (param) {
78 | return [];
79 | }));
80 | } else {
81 | addMany(param[0], instance);
82 | return $$Promise.resolved([]);
83 | }
84 | }
85 |
86 | exports.add = add;
87 | exports.addMany = addMany;
88 | exports.addFromFile = addFromFile;
89 | exports.destroyAll = destroyAll;
90 | exports.execute = execute;
91 | /* fs Not a pure module */
92 |
--------------------------------------------------------------------------------
/lib/js/src/Log.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Belt_Array = require("bs-platform/lib/js/belt_Array.js");
5 | var Belt_Option = require("bs-platform/lib/js/belt_Option.js");
6 | var Parser$AgdaMode = require("./Parser.bs.js");
7 | var Caml_splice_call = require("bs-platform/lib/js/caml_splice_call.js");
8 | var Request$AgdaMode = require("./Request.bs.js");
9 | var Response$AgdaMode = require("./Response.bs.js");
10 |
11 | function make(cmd) {
12 | return {
13 | request: cmd,
14 | response: {
15 | rawText: [],
16 | sexpression: [],
17 | response: [],
18 | error: []
19 | }
20 | };
21 | }
22 |
23 | function serialize(i, self) {
24 | var fold = function (text, title) {
25 | return " " + (String(title) + ("
\n\n\n" + (String(text) + "\n\n
\n \n")));
26 | };
27 | var quote = function (xs, title) {
28 | var xs$1 = fold(Caml_splice_call.spliceObjApply("\n", "concat", [Belt_Array.map(xs, (function (x) {
29 | return "```\n" + (String(x) + "\n```\n");
30 | }))]), title);
31 | return Caml_splice_call.spliceObjApply("\n", "concat", [Belt_Array.map(xs$1.split(/\n/), (function (__x) {
32 | return Belt_Option.mapWithDefault(__x, "", (function (x) {
33 | return " " + x;
34 | }));
35 | }))]);
36 | };
37 | var request = Request$AgdaMode.toString(self.request);
38 | var rawText = quote(self.response.rawText, "raw text");
39 | var sexpression = quote(Belt_Array.map(self.response.sexpression, Parser$AgdaMode.SExpression.toString), "s-expression");
40 | var response = quote(Belt_Array.map(self.response.response, Response$AgdaMode.toString), "response");
41 | var error = quote(Belt_Array.map(self.response.error, Parser$AgdaMode.$$Error.toString), "error");
42 | return "" + (String(i) + (". **" + (String(request) + ("**\n" + (String(rawText) + ("\n" + (String(sexpression) + ("\n" + (String(response) + ("\n" + (String(error) + "\n")))))))))));
43 | }
44 |
45 | var Entry = {
46 | make: make,
47 | serialize: serialize
48 | };
49 |
50 | function createEntry(cmd, log) {
51 | var entry = make(cmd);
52 | log.push(entry);
53 | return /* () */0;
54 | }
55 |
56 | function updateLatestEntry(f, log) {
57 | var n = log.length;
58 | var lastEntry = Belt_Array.get(log, n - 1 | 0);
59 | return Belt_Option.forEach(lastEntry, f);
60 | }
61 |
62 | function logRawText(text) {
63 | return (function (param) {
64 | return updateLatestEntry((function (entry) {
65 | entry.response.rawText.push(text);
66 | return /* () */0;
67 | }), param);
68 | });
69 | }
70 |
71 | function logSExpression(text) {
72 | return (function (param) {
73 | return updateLatestEntry((function (entry) {
74 | entry.response.sexpression.push(text);
75 | return /* () */0;
76 | }), param);
77 | });
78 | }
79 |
80 | function logResponse(text) {
81 | return (function (param) {
82 | return updateLatestEntry((function (entry) {
83 | entry.response.response.push(text);
84 | return /* () */0;
85 | }), param);
86 | });
87 | }
88 |
89 | function logError(text) {
90 | return (function (param) {
91 | return updateLatestEntry((function (log) {
92 | log.response.error.push(text);
93 | return /* () */0;
94 | }), param);
95 | });
96 | }
97 |
98 | function serialize$1(x) {
99 | return Caml_splice_call.spliceObjApply("\n", "concat", [Belt_Array.mapWithIndex(x, serialize)]);
100 | }
101 |
102 | exports.Entry = Entry;
103 | exports.createEntry = createEntry;
104 | exports.updateLatestEntry = updateLatestEntry;
105 | exports.logRawText = logRawText;
106 | exports.logSExpression = logSExpression;
107 | exports.logResponse = logResponse;
108 | exports.logError = logError;
109 | exports.serialize = serialize$1;
110 | /* Parser-AgdaMode Not a pure module */
111 |
--------------------------------------------------------------------------------
/lib/js/src/View/Hook.bs.js:
--------------------------------------------------------------------------------
1 | // Generated by BUCKLESCRIPT, PLEASE EDIT WITH CARE
2 | 'use strict';
3 |
4 | var Curry = require("bs-platform/lib/js/curry.js");
5 | var React = require("react");
6 | var Rebase = require("@glennsl/rebase/lib/js/src/Rebase.bs.js");
7 | var Channel$AgdaMode = require("../Util/Channel.bs.js");
8 |
9 | function useDidUpdateEffect(f, inputs) {
10 | var didMountRef = React.useRef(false);
11 | React.useEffect((function () {
12 | if (didMountRef.current) {
13 | return Curry._1(f, /* () */0);
14 | } else {
15 | didMountRef.current = true;
16 | return ;
17 | }
18 | }), inputs);
19 | return /* () */0;
20 | }
21 |
22 | function useDidUpdateEffect2(f, param) {
23 | var didMountRef = React.useRef(false);
24 | React.useEffect((function () {
25 | if (didMountRef.current) {
26 | return Curry._1(f, /* () */0);
27 | } else {
28 | didMountRef.current = true;
29 | return ;
30 | }
31 | }), /* tuple */[
32 | param[0],
33 | param[1]
34 | ]);
35 | return /* () */0;
36 | }
37 |
38 | function useState(init) {
39 | var match = React.useState((function () {
40 | return init;
41 | }));
42 | var setState = match[1];
43 | var setState$prime = function (value) {
44 | return Curry._1(setState, (function (param) {
45 | return value;
46 | }));
47 | };
48 | return /* tuple */[
49 | match[0],
50 | setState$prime
51 | ];
52 | }
53 |
54 | function useAtomListener(listener) {
55 | React.useEffect((function () {
56 | var destructor = Curry._1(listener, /* () */0);
57 | return (function (param) {
58 | destructor.dispose();
59 | return /* () */0;
60 | });
61 | }));
62 | return /* () */0;
63 | }
64 |
65 | function useAtomListenerWhen(listener, shouldListen) {
66 | var match = React.useState((function () {
67 | return ;
68 | }));
69 | var setDestructor = match[1];
70 | var destructor = match[0];
71 | React.useEffect((function () {
72 | if (shouldListen) {
73 | var destructor$1 = Curry._1(listener, /* () */0);
74 | Curry._1(setDestructor, (function (param) {
75 | return (function (param) {
76 | destructor$1.dispose();
77 | return /* () */0;
78 | });
79 | }));
80 | } else {
81 | Rebase.$$Option.forEach((function (f) {
82 | return Curry._1(f, /* () */0);
83 | }), destructor);
84 | }
85 | return destructor;
86 | }), [shouldListen]);
87 | return /* () */0;
88 | }
89 |
90 | function useListenWhen(listener, shouldListen) {
91 | var match = React.useState((function () {
92 | return ;
93 | }));
94 | var setDestructor = match[1];
95 | var destructor = match[0];
96 | React.useEffect((function () {
97 | if (shouldListen) {
98 | Curry._1(setDestructor, (function (param) {
99 | return Curry._1(listener, /* () */0);
100 | }));
101 | } else {
102 | Rebase.$$Option.forEach((function (f) {
103 | Curry._1(f, /* () */0);
104 | return Curry._1(setDestructor, (function (param) {
105 | return ;
106 | }));
107 | }), destructor);
108 | }
109 | return ;
110 | }), [shouldListen]);
111 | return /* () */0;
112 | }
113 |
114 | function useChannel(callback, channel) {
115 | React.useEffect((function () {
116 | Channel$AgdaMode.recv(callback, channel);
117 | return ;
118 | }), []);
119 | return /* () */0;
120 | }
121 |
122 | exports.useDidUpdateEffect = useDidUpdateEffect;
123 | exports.useDidUpdateEffect2 = useDidUpdateEffect2;
124 | exports.useState = useState;
125 | exports.useAtomListener = useAtomListener;
126 | exports.useAtomListenerWhen = useAtomListenerWhen;
127 | exports.useListenWhen = useListenWhen;
128 | exports.useChannel = useChannel;
129 | /* react Not a pure module */
130 |
--------------------------------------------------------------------------------