[Developer] Rewrite web editor integration to use Monaco instead of ACE

This commit is contained in:
Marc Durdin 2018-09-03 10:06:26 +10:00
parent 532bcee7c5
commit 79d45eff40
6 changed files with 319 additions and 272 deletions

View file

@ -57,20 +57,40 @@ unit UfrmDebug; // I3323 // I3306
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, Grids, ComCtrls, CaptionPanel, Buttons,
StdCtrls, debugging, debugkeyboard, UfrmMDIEditor, PaintPanel, Menus,
HintLabel, debugdeadkeys, UserMessages,
UframeTextEditor,
System.Classes,
System.Generics.Collections,
System.SysUtils,
Vcl.AppEvnts,
Vcl.Buttons,
Vcl.ComCtrls,
Vcl.Controls,
Vcl.Dialogs,
Vcl.ExtCtrls,
Vcl.Forms,
Vcl.Graphics,
Vcl.Grids,
Vcl.Menus,
Vcl.StdCtrls,
Winapi.Messages,
Winapi.Windows,
CaptionPanel,
debugdeadkeys,
debugging,
debugkeyboard,
DebugUtils,
keymanapi_TLB, msctf, UfrmTike,
KeymanDeveloperDebuggerMemo;
HintLabel,
PaintPanel,
keymanapi_TLB,
KeymanDeveloperDebuggerMemo,
msctf,
UframeTextEditor,
UfrmMDIEditor,
UfrmTike,
UserMessages;
type
TUpdateParColourEvent = procedure(Sender: TObject; ALine: Integer; ALineType: TParColourLineType) of object;
TBreakpointRange = 0..99;
TDebugLineEvent = procedure(Sender: TObject; ALine: Integer) of object;
TDebugUIStatus = (duiInvalid, duiPaused, duiFocusedForInput, duiReadyForInput, duiReceivingEvents, duiDebugging,
duiClosing, duiDebuggingOutput, duiTest);
@ -87,6 +107,8 @@ type
property TrueLineNumber: Integer read GetTrueLineNumber write SetTrueLineNumber;
end;
TDebugBreakpoints = class(TObjectList<TDebugBreakpoint>);
TfrmDebug = class(TTikeForm)
memo: TKeymanDeveloperDebuggerMemo;
mnuPopup: TPopupMenu; // I4796
@ -115,7 +137,7 @@ type
private
FDebugVisible: Boolean;
FBreakpoint: array[TBreakpointRange] of TDebugBreakpoint;
FBreakpoints: TDebugBreakpoints;
FRunning, FFoundBreakpoint, FForceKeyboard: Boolean;
FFileName: string;
//FEditor: TfrmTikeEditor;
@ -163,8 +185,6 @@ type
procedure KeymanGetContext(var Message: TMessage);
procedure UpdateParColour(Line: Integer; LineType: TParColourLineType);
procedure AddDebug(s: WideString);
{ Debug panel functions }
@ -174,10 +194,13 @@ type
private
FANSITest: Boolean;
FEditorMemo: TframeTextEditor;
FOnUpdateParColour: TUpdateParColourEvent;
FDebugFileName: WideString;
FSingleStepMode: Boolean;
FCanDebug: Boolean;
FOnUpdateExecutionPoint: TDebugLineEvent;
FOnClearBreakpoint: TDebugLineEvent;
FOnSetBreakpoint: TDebugLineEvent;
procedure SetANSITest(const Value: Boolean);
procedure UpdateANSITest;
procedure ClearDeadkeys;
@ -216,13 +239,14 @@ type
procedure SetBreakpoint(ALine: Integer);
procedure ClearBreakpoint(ALine: Integer);
procedure ToggleBreakpoint(ALine: Integer);
function IsBreakPointLine(ALine: Integer): Boolean;
function IsExecutionPointLine(ALine: Integer): Boolean;
function GetDebugKeyboard: TDebugKeyboard;
property CanDebug: Boolean read FCanDebug write FCanDebug;
property CanDebug: Boolean read FCanDebug write FCanDebug;
property UIStatus: TDebugUIStatus read FUIStatus write SetUIStatus;
property SingleStepMode: Boolean read FSingleStepMode write SetSingleStepMode;
@ -250,7 +274,9 @@ type
property CurrentEvent: TDebugEvent read GetCurrentEvent;
property OnUpdateParColour: TUpdateParColourEvent read FOnUpdateParColour write FOnUpdateParColour;
property OnSetBreakpoint: TDebugLineEvent read FOnSetBreakpoint write FOnSetBreakpoint;
property OnClearBreakpoint: TDebugLineEvent read FOnClearBreakpoint write FOnClearBreakpoint;
property OnUpdateExecutionPoint: TDebugLineEvent read FOnUpdateExecutionPoint write FOnUpdateExecutionPoint;
end;
implementation
@ -317,6 +343,7 @@ begin
// InitMSCTF; // I3655
TDebugUtils.GetActiveTSFProfile(FLastActiveProfile); // I4331
FBreakpoints := TDebugBreakpoints.Create;
FExecutionPointLine := -1;
//InitControlCaptions;
@ -331,11 +358,9 @@ begin
end;
procedure TfrmDebug.FormDestroy(Sender: TObject);
var
i: TBreakpointRange;
begin
ResetDebug;
for i := Low(TBreakpointRange) to High(TBreakpointRange) do FBreakpoint[i].Free;
FBreakpoints.Free;
FEvents.Free;
//UninitControlCaptions;
//UninitSystemKeyboard;
@ -722,7 +747,7 @@ end;
procedure TfrmDebug.WMUSERUpdateForceKeyboard(var Message: TMessage); // I4767
procedure FailTidyUp;
begin
Windows.SetFocus(0);
Winapi.Windows.SetFocus(0);
HideDebugForm;
FForceKeyboard := False;
end;
@ -763,8 +788,8 @@ begin
if FWasStarted then // I3283 // I3503
begin
// Give Keyman Engine a chance to correctly attach focus after it starts
Windows.SetFocus(0);
Windows.SetFocus(memo.Handle);
Winapi.Windows.SetFocus(0);
Winapi.Windows.SetFocus(memo.Handle);
Exit;
end;
@ -784,7 +809,7 @@ begin
{Keyman_Exit; I3283 // I3503
if not Keyman_Initialise(Application.MainForm.Handle, True) or not Keyman_ForceKeyboard(FFileName) then
begin}
Windows.SetFocus(0);
Winapi.Windows.SetFocus(0);
HideDebugForm;
ShowMessage('Unable to start debugger - please make sure that Keyman Developer is correctly installed (the error code was '+IntToHex(FLastError, 8)+').'); // I3173 // I3504
//(Editor as TfrmEditor).HideDebugForm;
@ -1017,59 +1042,49 @@ end;
procedure TfrmDebug.SetBreakpoint(ALine: Integer);
var
i: TBreakpointRange;
j: Integer;
b: TDebugBreakpoint;
begin
j := -1;
for i := Low(TBreakpointRange) to High(TBreakpointRange) do
begin
if Assigned(FBreakpoint[i]) then
begin
if (FBreakpoint[i].TrueLineNumber = ALine) then Exit;
end
else if j = -1 then j := i;
end;
for b in FBreakpoints do
if b.TrueLineNumber = ALine then
Exit;
if j = -1 then
begin
ShowMessage('You can only have a maximum of '+
IntToStr(High(TBreakpointRange)-Low(TBreakpointRange))+' breakpoints.');
Exit;
end;
b := TDebugBreakpoint.Create(EditorMemo);
b.TrueLineNumber := ALine;
FBreakpoints.Add(b);
FBreakpoint[j] := TDebugBreakpoint.Create(EditorMemo);
FBreakpoint[j].TrueLineNumber := ALine;
UpdateParColour(ALine, pcltBreakpoint);
if Assigned(FOnSetBreakpoint) then
FOnSetBreakpoint(Self, ALine);
end;
procedure TfrmDebug.ClearBreakpoint(ALine: Integer);
var
i: TBreakpointRange;
b: TDebugBreakpoint;
begin
for i := Low(TBreakpointRange) to High(TBreakpointRange) do
if Assigned(FBreakpoint[i]) and (FBreakpoint[i].TrueLineNumber = ALine) then
for b in FBreakpoints do
if b.TrueLineNumber = ALine then
begin
FBreakpoint[i].Free;
FBreakpoint[i] := nil;
if IsExecutionPointLine(ALine)
then UpdateParColour(ALine, pcltExecutionPoint)
else UpdateParColour(ALine, pcltNone);
FBreakpoints.Remove(b);
if Assigned(FOnClearBreakpoint) then
FOnClearBreakpoint(Self, ALine);
Exit;
end;
end;
procedure TfrmDebug.ToggleBreakpoint(ALine: Integer);
begin
if IsBreakpointLine(ALine)
then ClearBreakpoint(ALine)
else SetBreakpoint(ALine)
end;
function TfrmDebug.IsBreakPointLine(ALine: Integer): Boolean;
var
i: Integer;
b: TDebugBreakpoint;
begin
Result := True;
for i := Low(TBreakpointRange) to High(TBreakpointRange) do
begin
if FBreakpoint[i] <> nil then
if FBreakpoint[i].TrueLineNumber = ALine then
Exit;
end;
Result := False;
for b in FBreakpoints do
if b.TrueLineNumber = ALine then
Exit(True);
Exit(False);
end;
{-------------------------------------------------------------------------------
@ -1082,28 +1097,21 @@ begin
end;
procedure TfrmDebug.SetExecutionPointLine(ALine: Integer);
var
oldline: Integer;
begin
oldline := FExecutionPointLine;
FExecutionPointLine := ALine;
if IsBreakPointLine(oldline)
then UpdateParColour(oldline, pcltBreakpoint)
else UpdateParColour(oldline, pcltNone);
if FRunning then Exit;
if FExecutionPointLine >= 0 then
UpdateParColour(FExecutionPointLine, pcltExecutionPoint);
if FRunning and (FExecutionPointLine >= 0) then Exit;
if Assigned(FOnUpdateExecutionPoint) then
FOnUpdateExecutionPoint(Self, ALine);
end;
procedure TfrmDebug.ListBreakpoints;
var
i: Integer;
b: TDebugBreakpoint;
begin
memo.Clear;
//frmDebugStatus.DeadKeys.ClearDeadkeys; I1699
for i := 0 to 99 do
if Assigned(FBreakPoint[i]) and (FBreakpoint[i].TrueLineNumber >= 0) then
UpdateParColour(FBreakpoint[i].TrueLineNumber, pcltBreakpoint);
for b in FBreakpoints do
if Assigned(FOnSetBreakpoint) then
FOnSetBreakpoint(Self, b.TrueLineNumber);
end;
@ -1178,12 +1186,6 @@ begin
(frmDebugStatus as TfrmDebugStatus).DisplayFont := FFont;
end;
procedure TfrmDebug.UpdateParColour(Line: Integer; LineType: TParColourLineType);
begin
if Assigned(FOnUpdateParColour) then
FOnUpdateParColour(Self, Line, LineType);
end;
procedure TfrmDebug.Pause;
begin
if UIStatus = duiTest then Exit;

View file

@ -437,6 +437,8 @@ type
procedure TouchLayoutModified(Sender: TObject);
procedure FocusTabTouchLayout;
procedure EditorBreakpointClicked(Sender: TObject; ALine: Integer);
procedure FillFeatureGrid;
procedure CheckParserSourceMove(Changed: Boolean);
procedure ToggleWinLanguagesRow(Y: Integer);
@ -488,7 +490,9 @@ type
procedure StopDebugging;
function PrepareForBuild(var DebugReset: Boolean): Boolean; // I4504
procedure UpdateParColour(Sender: TObject; ALine: Integer; ALineType: TParColourLineType);
procedure DebugSetBreakpoint(Sender: TObject; ALine: Integer);
procedure DebugClearBreakpoint(Sender: TObject; ALine: Integer);
procedure DebugUpdateExecutionPoint(Sender: TObject; ALine: Integer);
function HasSubfilename(const Filename: string): Boolean; override; // I4081
@ -626,6 +630,7 @@ begin
frameSource.EditorFormat := efKMN;
frameSource.Visible := True;
frameSource.OnChanged := SourceChanged;
frameSource.OnBreakpointClicked := EditorBreakpointClicked;
frameSource.TextFileFormat := FTextFileFormat;
pages.ActivePage := pageDetails;
@ -702,6 +707,13 @@ end;
{ General Functions }
{-----------------------------------------------------------------------------}
procedure TfrmKeymanWizard.EditorBreakpointClicked(Sender: TObject;
ALine: Integer);
begin
if Assigned(FDebugForm) then
FDebugForm.ToggleBreakpoint(ALine);
end;
procedure TfrmKeymanWizard.EnableControls;
begin
BitmapEnableControls;
@ -1478,11 +1490,6 @@ begin
tmrUpdateCharacterMap.Enabled := True;
end;
procedure TfrmKeymanWizard.UpdateParColour(Sender: TObject; ALine: Integer; ALineType: TParColourLineType);
begin
frameSource.UpdateParColour(ALine, ALineType);
end;
procedure TfrmKeymanWizard.SelectVKey(VKey: Integer);
begin
kbdLayout.SelectedKey := kbdLayout.Keys.ItemsByUSVK[VKey];
@ -1942,6 +1949,22 @@ begin
frmKeymanDeveloper.cbTextFileFormat.ItemIndex := Ord(TextFileFormat);
end;
procedure TfrmKeymanWizard.DebugClearBreakpoint(Sender: TObject;
ALine: Integer);
begin
frameSource.DebugClearBreakpoint(ALine);
end;
procedure TfrmKeymanWizard.DebugSetBreakpoint(Sender: TObject; ALine: Integer);
begin
frameSource.DebugSetBreakpoint(ALine);
end;
procedure TfrmKeymanWizard.DebugUpdateExecutionPoint(Sender: TObject; ALine: Integer);
begin
frameSource.DebugUpdateExecutionPoint(ALine);
end;
procedure TfrmKeymanWizard.DoFocus(control: TWinControl);
begin
if control.Enabled and control.Visible and control.Showing and control.CanFocus then
@ -2835,7 +2858,9 @@ begin
FDebugForm.BorderStyle := bsNone;
FDebugForm.Parent := panDebugWindowHost;
FDebugForm.Align := alClient;
FDebugForm.OnUpdateParColour := UpdateParColour;
FDebugForm.OnSetBreakpoint := DebugSetBreakpoint;
FDebugForm.OnClearBreakpoint := DebugClearBreakpoint;
FDebugForm.OnUpdateExecutionPoint := DebugUpdateExecutionPoint;
FDebugForm.Visible := True;

View file

@ -32,7 +32,7 @@ uses
UserMessages;
type
TParColourLineType = (pcltNone, pcltBreakpoint, pcltExecutionPoint, pcltError);
TEditorBreakpointClickedEvent = procedure(Sender: TObject; ALine: Integer) of object;
TframeTextEditor = class(TTIKEForm, IKMDEditActions, IKMDSearchActions, IKMDTextEditorActions)
lstImages: TMenuImgList;
@ -59,6 +59,7 @@ type
cef: TframeCEFHost;
FFilename: string;
FOnBreakpointClicked: TEditorBreakpointClickedEvent;
procedure RefreshOptions;
function GetText: WideString;
@ -91,9 +92,11 @@ type
procedure LoadFileInBrowser(const AData: string);
procedure UpdateInsertState(const AMode: string);
procedure ExecuteCommand(const command: string; const parameters: TJSONValue = nil);
procedure ExecuteLineCommand(ALine: Integer; const command: string);
procedure UpdateToken(command: string);
procedure SetCursorPosition(AColumn, ARow: Integer);
procedure cefLoadEnd(Sender: TObject);
procedure DoBreakpointClicked(const line: string);
protected
function GetHelpTopic: string; override;
@ -136,7 +139,11 @@ type
public
{ Public declarations }
procedure UpdateParColour(par: Integer; LineType: TParColourLineType);
procedure DebugSetBreakpoint(ALine: Integer);
procedure DebugClearBreakpoint(ALine: Integer);
procedure DebugUpdateExecutionPoint(ALine: Integer);
property OnBreakpointClicked: TEditorBreakpointClickedEvent read FOnBreakpointClicked write FOnBreakpointClicked;
procedure SetFocus; override;
procedure FindError(ln: Integer);
@ -170,6 +177,7 @@ implementation
uses
System.TypInfo,
Vcl.Clipbrd,
Keyman.Developer.System.HelpTopics,
dmActionsMain,
@ -785,6 +793,21 @@ begin
cef.cef.ClipboardCut;
end;
procedure TframeTextEditor.DebugClearBreakpoint(ALine: Integer);
begin
ExecuteLineCommand(ALine, 'clearBreakpoint');
end;
procedure TframeTextEditor.DebugSetBreakpoint(ALine: Integer);
begin
ExecuteLineCommand(ALine, 'setBreakpoint');
end;
procedure TframeTextEditor.DebugUpdateExecutionPoint(ALine: Integer);
begin
ExecuteLineCommand(ALine, 'updateExecutionPoint');
end;
procedure TframeTextEditor.PasteFromClipboard;
begin
cef.cef.ClipboardPaste;
@ -832,28 +855,32 @@ begin
end;
end;
procedure TframeTextEditor.FindError(ln: Integer);
procedure TframeTextEditor.ExecuteLineCommand(ALine: Integer;
const command: string);
var
v: TJSONNumber;
begin
v := TJSONNumber.Create(ALine);
try
ExecuteCommand(command, v);
finally
v.Free;
end;
end;
procedure TframeTextEditor.FindError(ln: Integer);
begin
ClearError;
if (ln <= 0) then Exit;
v := TJSONNumber.Create(ln);
try
ExecuteCommand('highlightError', v);
SetSelectedRow(ln);
finally
v.Free;
end;
ExecuteLineCommand(ln, 'highlightError');
SetSelectedRow(ln);
end;
function TframeTextEditor.OffsetToLine(Offset: Integer): Integer; // I4083
var
line: Integer;
begin
Result := 0;
with TStringList.Create do
try
Text := Self.GetText;
@ -879,40 +906,49 @@ end;
procedure TframeTextEditor.FireCommand(const commands: TStringList);
var
i: Integer;
command: string;
command0, command: string;
commandParams: TArray<string>;
begin
i := 0;
while i < commands.Count do
begin
command := commands[i];
command0 := commands[i];
commandParams := command0.Split([',']);
command := commandParams[0];
if command = 'modified' then Changed // I3948
else if command = 'undo-disable' then FCanUndo := False
else if command = 'undo-enable' then FCanUndo := True
else if command = 'redo-disable' then FCanRedo := False
else if command = 'redo-enable' then FCanRedo := True
else if command = 'has-selection' then FHasSelection := True
else if command = 'no-selection' then FHasSelection := False
else if command.StartsWith('insert-mode,') then
else if command = 'breakpoint-clicked' then DoBreakpointClicked(commandParams[1])
else if command0.StartsWith('insert-mode,') then
begin
UpdateInsertState(command.Substring('insert-mode,'.Length));
UpdateInsertState(command0.Substring('insert-mode,'.Length));
end
else if command.StartsWith('location,') then
else if command0.StartsWith('location,') then
begin
UpdateState(command.Substring('location,'.Length));
UpdateState(command0.Substring('location,'.Length));
end
else if command.StartsWith('token,') then
else if command0.StartsWith('token,') then
begin
UpdateToken(command.Substring('token,'.Length));
UpdateToken(command0.Substring('token,'.Length));
end
else ShowMessage('keyman:'+commands.Text);
Inc(i);
end;
end;
procedure TframeTextEditor.DoBreakpointClicked(const line: string);
begin
if Assigned(FOnBreakpointClicked) then
FOnBreakpointClicked(Self, StrToInt(line));
end;
procedure TframeTextEditor.UpdateToken(command: string);
var
n, col: Integer;
col: Integer;
line: string;
x, tx: Integer;
token, prevtoken: WideString;
@ -968,23 +1004,6 @@ begin
end;
end;
procedure TframeTextEditor.UpdateParColour(par: Integer; LineType: TParColourLineType);
var
j: TJSONObject;
begin
if par < 0 then Exit;
j := TJSONObject.Create;
try
j.AddPair('row', TJSONNumber.Create(par));
if LineType <> pcltNone then
j.AddPair('style', TJSONString.Create(GetEnumName(TypeInfo(TParColourLineType), Ord(LineType))));
ExecuteCommand('setRowColor', j);
finally
j.Free;
end;
end;
function TframeTextEditor.GetHelpTopic: string;
begin
if FEditorFormat <> efKMN then

View file

@ -11,27 +11,14 @@ html, body {
left: 0;
}
.ace_marker-layer .km_error,
.ace_marker-layer .km_pcltBreakpoint,
.ace_marker-layer .km_pcltExecutionPoint,
.ace_marker-layer .km_pcltError {
position: absolute;
z-index: 3;
}
/*.ace-chrome*/ .ace_marker-layer .km_error {
.km_error {
background: rgb(255, 128, 128);
}
/*.ace-chrome*/ .ace_marker-layer .km_pcltBreakpoint {
background-color: forestgreen;
.km_breakpoint {
background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cdefs%3E%3Cstyle%3E.icon-canvas-transparent,.icon-vs-out{fill:%23f6f6f6}.icon-canvas-transparent{opacity:0}.icon-vs-red{fill:%23e51400}%3C/style%3E%3C/defs%3E%3Ctitle%3Ebreakpoint%3C/title%3E%3Cpath class='icon-canvas-transparent' d='M16 0v16H0V0z' id='canvas'/%3E%3Cpath class='icon-vs-red' d='M11.789 8A3.789 3.789 0 1 1 8 4.211 3.788 3.788 0 0 1 11.789 8z' id='iconBg'/%3E%3C/svg%3E") 50% no-repeat
}
/*.ace-chrome*/ .ace_marker-layer .km_pcltExecutionPoint {
.km_executionPoint {
background-color: deepskyblue;
}
/*.ace-chrome*/ .ace_marker-layer .km_pcltError {
background: rgb(255, 128, 128);
}

View file

@ -1,3 +1,11 @@
/*
* Editor using monaco
*
* Helpful references:
* https://microsoft.github.io/monaco-editor/api/index.html
* https://blog.expo.io/building-a-code-editor-with-monaco-f84b3a06deaf
*/
window.editorGlobalContext = {
loading: false
};
@ -5,7 +13,8 @@ window.editorGlobalContext = {
(function(context) {
var editor = null;
var errorRange = null;
var ranges = [];
var executionPoint = [];
var breakpoints = [];
let params = (new URL(location)).searchParams;
let filename = params.get('filename');
let mode = params.get('mode');
@ -13,103 +22,153 @@ window.editorGlobalContext = {
if(!mode) {
mode = 'keyman';
}
/**
Initialize the editor
*/
require.config({ paths: { 'vs': '../lib/monaco/min/vs' } });
require(['vs/editor/editor.main'], function () {
editor = monaco.editor.create(document.getElementById('editor'), {
language: mode,
minimap: {
enabled: false
},
glyphMargin: true,
disableMonospaceOptimizations: true
});
$.get("/app/source/file",
{
Filename: filename
},
function (response) {
context.loading = true;
editor.setValue(response);
context.loading = false;
},
"text"
);
const model = editor.getModel();
model.onDidChangeContent(() => {
// Even when loading, we post back the data to the backend so we have an original version
$.post("/app/source/file", {
Filename: filename,
Data: model.getValue()
// delta.start, delta.end, delta.lines, delta.action
});
if (!context.loading) {
context.highlightError(); // clear the selected error
command('modified');
updateState();
}
});
editor.onDidChangeCursorPosition(updateState);
editor.onDidChangeCursorSelection(updateState);
editor.onMouseDown(e => {
if (e.target.type !== monaco.editor.MouseTargetType.GUTTER_GLYPH_MARGIN) {
return;
}
command('breakpoint-clicked,'+(e.target.position.lineNumber-1));
});
});
/* Search and replace interfaces */
context.searchFind = function() {
editor.commands.byName.find.exec(editor);
context.searchFind = function () {
editor.trigger('', 'actions.find');
};
context.searchFindNext = function() {
editor.commands.byName.selectOrFindNext.exec(editor);
editor.trigger('', 'editor.action.nextMatchFindAction');
};
context.searchReplace = function() {
editor.commands.byName.replace.exec(editor);
editor.trigger('', 'editor.action.startFindReplaceAction');
};
/* Edit command interfaces */
context.editUndo = function() {
editor.undo();
editor.model.undo();
};
context.editRedo = function() {
editor.redo();
editor.model.redo();
};
context.editSelectAll = function() {
editor.selectAll();
editor.setSelection(editor.model.getFullModelRange());
};
/* Row selection */
context.moveCursor = function (o) {
editor.moveCursorTo(o.row, o.column);
editor.selection.clearSelection();
editor.renderer.scrollCursorIntoView();
editor.setPosition({ column: o.column + 1, lineNumber: o.row + 1 });
editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), monaco.editor.ScrollType.Smooth);
};
/* Debug interactions */
context.setBreakpoint = function (row) {
if (!breakpoints[row]) {
breakpoints[row] = editor.deltaDecorations(
[],
[{ range: new monaco.Range(row + 1, 1, row + 1, 1), options: { isWholeLine: true, glyphMarginClassName: 'km_breakpoint' } }]
);
}
};
context.clearBreakpoint = function (row) {
if (breakpoints[row]) {
editor.deltaDecorations(breakpoints[row], []);
breakpoints[row] = null;
}
};
context.updateExecutionPoint = function (row) {
executionPoint = editor.deltaDecorations(
executionPoint,
row >= 0 ? [{ range: new monaco.Range(row + 1, 1, row + 1, 1), options: { isWholeLine: true, linesDecorationsClassName: 'km_executionPoint' } }] : []
);
context.moveCursor({ row: row, column: 0 });
};
/* Error highlighting */
context.setRowColor = function(o) {
if (o.style) {
var r = new ace.Range(o.row, 0, o.row, Infinity);
r.row = o.row;
r.id = editor.session.addMarker(r, 'km_'+o.style, "fullLine", false);
ranges.push(r);
} else {
ranges = ranges.filter(function (v) {
if (v.row !== o.row) {
return true;
}
editor.session.removeMarker(v.id);
return false;
});
context.highlightError = function (row) {
if (errorRange) {
editor.deltaDecorations(errorRange, []);
errorRange = null;
}
editor.moveCursorTo(o.row, 0);
editor.selection.clearSelection();
editor.renderer.scrollCursorIntoView();
};
context.highlightError = function(row) {
if(errorRange) {
editor.session.removeMarker(errorRange.id);
}
if(typeof row == 'number') {
errorRange = new ace.Range(row, 0, row, Infinity);
errorRange.id = editor.session.addMarker(errorRange, 'km_error', "fullLine", false);
if (typeof row == 'number') {
errorRange = editor.deltaDecorations([], [{ range: new monaco.Range(row + 1, 1, row + 1, 1), options: { isWholeLine: true, linesDecorationsClassName: 'km_error', className: 'km_error' } }]);
}
};
context.replaceSelection = function (o) {
let originalRange = new ace.Range(o.top, o.left, o.bottom, o.right);
let end = editor.session.doc.replace(originalRange, o.newText);
let newRange = new ace.Range(o.top, o.left, end.row, end.column);
editor.selection.setSelectionRange(newRange, false);
let r = new monaco.Range(o.top + 1, o.left + 1, o.bottom + 1, o.right + 1);
editor.setSelection(r);
editor.executeEdits("", [
{ range: r, text: o.newText }
]);
};
context.setText = function (text) {
let range = editor.session.selection.getRange();
let range = editor.getSelection();
context.loading = true;
editor.session.setValue(text);
editor.session.selection.setSelectionRange(range);
context.loading = false;
};
context.setText = function (text) {
let range = editor.session.selection.getRange();
context.loading = true;
editor.session.setValue(text);
editor.session.selection.setSelectionRange(range);
editor.setValue(text);
editor.setSelection(range);
context.loading = false;
};
/* Printing */
context.print = function () {
require("ace/config").loadModule("ace/ext/static_highlight", function (m) {
/****require("ace/config").loadModule("ace/ext/static_highlight", function (m) {
var result = m.renderSync(
editor.getValue(), editor.session.getMode(), editor.renderer.theme
);
@ -129,7 +188,7 @@ window.editorGlobalContext = {
}, 10);
};
document.body.appendChild(iframe);
});
});*/
};
/**
@ -162,84 +221,38 @@ window.editorGlobalContext = {
/**
*/
var updateState = function() {
command(editor.getSelectedText() == '' ? 'no-selection' : 'has-selection');
command(editor.session.getUndoManager().hasUndo() ? 'undo-enable' : 'undo-disable');
command(editor.session.getUndoManager().hasRedo() ? 'redo-enable' : 'redo-disable');
let r = editor.selection.getRange();
var n = editor.session.doc.getTextRange(editor.selection.getRange()).length;
if (!editor.selection.isBackwards()) n = -n;
command('location,' + r.start.row + ',' + r.start.column + ',' + r.end.row + ',' + r.end.column + ',' + n);
command('insert-mode,' + (editor.session.getOption('overwrite') ? 'Overwrite' : 'Insert'));
// command(editor.session.getUndoManager().isClean() ? 'modified' : 'not-modified');
var s = getTokenAtCursor();
if(s) {
command('token,'+s.column+','+encodeURIComponent(s.text));
var updateState = function () {
let s = editor.getSelection();
command(s.isEmpty() ? 'no-selection' : 'has-selection');
command(editor.model.canUndo() ? 'undo-enable' : 'undo-disable');
command(editor.model.canRedo() ? 'redo-enable' : 'redo-disable');
var n = editor.model.getValueInRange(s).length;
if (editor.getSelection().getDirection() == monaco.SelectionDirection.LTR) n = -n;
command('location,' + (s.startLineNumber-1) + ',' + (s.startColumn-1) + ',' + (s.endLineNumber-1) + ',' + (s.endColumn-1) + ',' + n);
//command('insert-mode,' + (editor.session.getOption('overwrite') ? 'Overwrite' : 'Insert'));
var token = getTokenAtCursor();
if (token) {
command('token,' + token.column + ',' + encodeURIComponent(token.text));
}
};
var getTokenAtCursor = function() {
var txt = editor.getSelectedText();
if(txt != '') {
var getTokenAtCursor = function () {
var txt = editor.model.getValueInRange(editor.getSelection());
if (txt != '') {
// We'll always return the first 100 characters of the selection and not
// do any manipulation here.
return {column:null,text:txt.substr(0, 99)};
return { column: null, text: txt.substr(0, 99) };
}
if(mode == 'keyman') {
if (mode == 'keyman') {
// Get the token under the cursor
var c = editor.session.selection.getCursor();
var line = editor.session.getLine(c.row);
return {column:c.column, text:line};
var c = editor.getSelection();
var line = editor.model.getLineContent(c.positionLineNumber);
return { column: c.positionColumn-1, text: line };
} else {
// Get the character under the cursor
return null;
}
};
/**
Initialize the editor
*/
$(function initialize() {
editor = ace.edit("editor");
// Remove Alt+ key bindings which may conflict with environment shortcuts, e.g. Alt+E, function keys
var reAltKeyBindings = /(^alt-[a-z0-9]|f[0-9]+)$/;
for (var key in editor.keyBinding.$defaultHandler.commandKeyBinding) {
if (key.match(reAltKeyBindings)) {
delete editor.keyBinding.$defaultHandler.commandKeyBinding[key];
}
}
editor.setTheme("ace/theme/xcode");
editor.session.selection.on('changeCursor', updateState);
editor.session.on('changeOverwrite', updateState);
editor.session.selection.on('changeSelection', updateState);
editor.session.on('change', function (delta) {
// Even when loading, we post back the data to the backend so we have an original version
$.post("/app/source/file", {
Filename: filename,
Data: editor.session.getValue()
// delta.start, delta.end, delta.lines, delta.action
});
if (!context.loading) {
context.highlightError(); // clear the selected error
command('modified');
updateState();
}
});
// Start reading the source file
$.get("/app/source/file", {
Filename: filename
}, function (response) {
context.loading = true;
editor.session.setValue(response);
editor.session.setMode("ace/mode/"+mode);
context.loading = false;
},
"text");
});
})(window.editorGlobalContext);

View file

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
@ -15,11 +15,12 @@
<link rel="stylesheet" type='text/css' href='editor.css' />
<script src="../lib/ace/ace.js" charset="utf-8"></script>
<script src="../lib/jquery/jquery-1.10.2.js"></script>
<script src="editor.js"></script>
</head>
<body>
<div id="editor"></div>
<script src="../lib/monaco/min/vs/loader.js"></script>
<script src="editor.js"></script>
</body>
</html>