Merge pull request #51 from WolframResearch/feature/add-rewrites-for-named-character-names

Add rewrites for named character names (to named characters)
This commit is contained in:
cc-wr 2019-09-09 16:43:35 -04:00 committed by GitHub
commit 3e9068fc4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 304 additions and 13 deletions

View File

@ -0,0 +1,84 @@
(************************************************
CompletionUtilities.wl
*************************************************
Description:
Utilities for the aiding in the
(auto-)completion of Wolfram Language
code
Symbols defined:
rewriteNamedCharacters
*************************************************)
(************************************
Get[] guard
*************************************)
If[
!TrueQ[WolframLanguageForJupyter`Private`$GotCompletionUtilities],
WolframLanguageForJupyter`Private`$GotCompletionUtilities = True;
(************************************
load required
WolframLanguageForJupyter
files
*************************************)
Get[FileNameJoin[{DirectoryName[$InputFileName], "Initialization.wl"}]]; (* unicodeNamedCharactersReplacements,
verticalEllipsis *)
(************************************
private symbols
*************************************)
(* begin the private context for WolframLanguageForJupyter *)
Begin["`Private`"];
(************************************
utilities for rewriting
Wolfram Language code
*************************************)
(* rewrite names (in a code string) into named characters *)
rewriteNamedCharacters[codeToAnalyze_?StringQ] :=
Module[
{codeUsingFullReplacements},
codeUsingFullReplacements =
StringReplace[
codeToAnalyze,
Normal @ unicodeNamedCharactersReplacements
];
If[
StringCount[
codeUsingFullReplacements,
verticalEllipsis | "\\["
] != 1,
Return[{codeUsingFullReplacements}];
];
Return[
Flatten[
StringCases[
codeUsingFullReplacements,
before___ ~~ name : ((verticalEllipsis | "\\[") ~~ rest__ ~~ EndOfString) :>
(
(StringJoin[before, #1] &) /@
Values[
KeySelect[
unicodeNamedCharactersReplacements,
StringMatchQ[#1, name ~~ ___] &
]
]
)
]
]
];
];
(* end the private context for WolframLanguageForJupyter *)
End[]; (* `Private` *)
(************************************
Get[] guard
*************************************)
] (* WolframLanguageForJupyter`Private`$GotCompletionUtilities *)

View File

@ -16,6 +16,8 @@ Symbols defined:
keyString,
baseString,
heartbeatString,
verticalEllipsis,
unicodeNamedCharactersReplacements,
ioPubString,
controlString,
inputString,
@ -61,6 +63,112 @@ If[
$Messages = {};
$Output = {}; *)
(************************************
discover the named unicode
characters and their names
*************************************)
(* the vertical ellipsis character *)
verticalEllipsis = FromCharacterCode[8942, "Unicode"];
(* pre-define the association of names and named characters as empty *)
unicodeNamedCharactersReplacements = Association[];
Block[
{
(* the absolute file name for "UnicodeCharacters.tr" *)
unicodeCharactersTRFileName,
(* raw data extracted from "UnicodeCharacters.tr" *)
charactersAndTheirNames
},
(* attempt to get the full location of "UnicodeCharacters.tr" *)
unicodeCharactersTRFileName = UsingFrontEnd[System`Dump`unicodeCharactersTR];
(* try again if using System`Dump`unicodeCharactersTR does not work *)
If[
!StringQ[unicodeCharactersTRFileName],
unicodeCharactersTRFileName =
UsingFrontEnd[
ToFileName[
FrontEnd`FileName[
{
$InstallationDirectory,
"SystemFiles",
"FrontEnd",
"TextResources"
},
"UnicodeCharacters.tr"
]
]
];
];
If[
StringQ[unicodeCharactersTRFileName],
charactersAndTheirNames =
(
(* parse the third item of a row (for a named character) into a list *)
ReplacePart[
#1,
3 ->
StringCases[
(* remove extraneous parentheses *)
StringTrim[
#1[[3]],
"(" | ")"
],
(* extract the escape sequences for the named character *)
Longest[escSeq : (Except[WhitespaceCharacter] ..)] :>
StringJoin[verticalEllipsis, StringTrim[escSeq, "$"], verticalEllipsis]
]
] &
) /@
(* only use lists with at least three items *)
Select[
(* parse the rows into their items (where each item is separated by two tabs) *)
(StringSplit[#1, "\t\t"] &) /@
(
(* split unicodeCharactersTRFileName into its lines *)
StringSplit[
Import[unicodeCharactersTRFileName, "String"],
"\n"
(* drop the first row *)
][[2 ;;]]
),
(Length[#1] >= 3) &
];
(* parse the data into an association of names and the named characters they correspond to *)
unicodeNamedCharactersReplacements =
(* sort the keys by string length *)
KeySort[
(* sort the keys in the default manner *)
KeySort[
(* drop "empty names" *)
KeyDrop[
(* make an association *)
Association[
(* create a list of rules of names and named characters *)
Thread[
Rule[
(Prepend[#1[[3]], #1[[2]]]),
FromCharacterCode[FromDigits[StringDrop[#1[[1]], 2], 16], "Unicode"]
]
] & /@ charactersAndTheirNames
],
{
StringJoin[Table[verticalEllipsis, {2}]],
"\\[]"
}
]
],
(StringLength[#1] < StringLength[#2]) &
];
];
];
(************************************
various important symbols
for use by
@ -101,13 +209,10 @@ If[
$outputSetToTraditionalForm := (Lookup[Options[$Output], FormatType] === TraditionalForm);
$outputSetToTeXForm := (Lookup[Options[$Output], FormatType] === TeXForm);
$trueFormatType :=
Which[
If[
$outputSetToTraditionalForm,
TraditionalForm,
$outputSetToTeXForm,
TeXForm,
True,
Identity
If[$outputSetToTeXForm, TeXForm, #&]
];
(* obtain details on how to connect to Jupyter, from Jupyter's invocation of "KernelForWolframLanguageForJupyter.wl" *)

View File

@ -36,7 +36,7 @@ Get[FileNameJoin[{DirectoryName[$InputFileName], "Initialization.wl"}]]; (* init
Get[FileNameJoin[{DirectoryName[$InputFileName], "SocketUtilities.wl"}]]; (* sendFrame *)
Get[FileNameJoin[{DirectoryName[$InputFileName], "MessagingUtilities.wl"}]]; (* getFrameAssoc, createReplyFrame *)
Get[FileNameJoin[{DirectoryName[$InputFileName], "RequestHandlers.wl"}]]; (* executeRequestHandler *)
Get[FileNameJoin[{DirectoryName[$InputFileName], "RequestHandlers.wl"}]]; (* executeRequestHandler, completeRequestHandler *)
(************************************
private symbols
@ -96,6 +96,10 @@ loop[] :=
"execute_request",
(* executeRequestHandler will read and update loopState *)
executeRequestHandler[];,
(* use the tab-completion functionality to rewrite named character names *)
"complete_request",
(* completeRequestHandler will read and update loopState *)
completeRequestHandler[];,
(* if asking the kernel to shutdown, set doShutdown to True *)
"shutdown_request",
loopState["replyMsgType"] = "shutdown_reply";

View File

@ -209,10 +209,7 @@ If[
be identical to the string result of an InputForm-wrapped expression itself *)
({"&#", ToString[#1], ";"} & /@
ToCharacterCode[
(* toStringUsingOutput[result] *)
Quiet[
ToString[If[!isTeXWrapped, $trueFormatType[result], result]]
],
(* toStringUsingOutput[result] *) ToString[If[!isTeXWrapped, $trueFormatType[result], result]],
"Unicode"
]),

View File

@ -5,7 +5,8 @@ Description:
Handlers for message frames of type
"x_request" arriving from Jupyter
Symbols defined:
executeRequestHandler
executeRequestHandler,
completeRequestHandler
*************************************************)
(************************************
@ -30,7 +31,10 @@ If[
Get[FileNameJoin[{DirectoryName[$InputFileName], "EvaluationUtilities.wl"}]]; (* simulatedEvaluate *)
Get[FileNameJoin[{DirectoryName[$InputFileName], "OutputHandlingUtilities.wl"}]]; (* textQ, toOutText, toOutImage *)
Get[FileNameJoin[{DirectoryName[$InputFileName], "OutputHandlingUtilities.wl"}]]; (* textQ, toOutText, toOutImage,
containsPUAQ *)
Get[FileNameJoin[{DirectoryName[$InputFileName], "CompletionUtilities.wl"}]]; (* rewriteNamedCharacters *)
(************************************
private symbols
@ -69,7 +73,6 @@ If[
(* set the content of the reply to information about WolframLanguageForJupyter's execution of the input *)
loopState["replyContent"] =
ExportString[
(* kind of self-explanatory *)
Association[
"status" -> "ok",
"execution_count" -> loopState["executionCount"],
@ -278,6 +281,54 @@ If[
loopState["executionCount"] += totalResult["ConsumedIndices"];
];
(************************************
handler for complete_requests
*************************************)
(* handle complete_request messages frames received on the shell socket *)
completeRequestHandler[] :=
Module[
{
(* for storing the code string to offer completion suggestions on *)
codeStr
},
(* get the code string to rewrite the named characters of, ending at the cursor *)
codeStr =
StringTake[
loopState["frameAssoc"]["content"]["code"],
{
1,
loopState["frameAssoc"]["content"]["cursor_pos"]
}
];
(* set the appropriate reply type *)
loopState["replyMsgType"] = "complete_reply";
(* set the content of the reply to a list of rewrites for any named characters in the code string *)
loopState["replyContent"] =
ByteArrayToString[
ExportByteArray[
Association[
"matches" ->
DeleteDuplicates[
Prepend[
Select[
rewriteNamedCharacters[codeStr],
(!containsPUAQ[#1])&
],
codeStr
]
],
"cursor_start" -> 0,
"cursor_end" -> StringLength[codeStr],
"metadata" -> {},
"status" -> "ok"
],
"JSON",
"Compact" -> True
]
];
];
(* end the private context for WolframLanguageForJupyter *)
End[]; (* `Private` *)

50
extras/custom.js Normal file
View File

@ -0,0 +1,50 @@
/* (adapted) from https://stackoverflow.com/a/19961519 by Erik Aigner */
HTMLTextAreaElement.prototype.insertAtCaret = function (text) {
text = text || '';
if(document.selection) {
// IE
this.focus();
var sel = document.selection.createRange();
sel.text = text;
}
else if(this.selectionStart || this.selectionStart === 0) {
// Others
var startPos = this.selectionStart;
var endPos = this.selectionEnd;
this.value = this.value.substring(0, startPos) + text + this.value.substring(endPos, this.value.length);
this.selectionStart = startPos + text.length;
this.selectionEnd = startPos + text.length;
}
else {
this.value += text;
}
};
/* (adapted) from https://stackoverflow.com/a/51114347 by bambam */
function redirectEsc(event) {
if(event.which == 27)
{
event.target.insertAtCaret(
/* the vertical ellipsis character */
String.fromCharCode(8942)
);
event.stopImmediatePropagation();
}
}
/* (adapted) from https://stackoverflow.com/a/51114347 by bambam */
var observer = new MutationObserver(function(mutations) {
Array.from(
document.querySelectorAll('.input_area')
).forEach(
textarea =>
{
textarea.removeEventListener('keydown', redirectEsc);
textarea.addEventListener('keydown', redirectEsc);
}
);
});
observer.observe(document, {childList:true, subtree:true});