Skip to content

Commit

Permalink
restore cursor pos after formatting (#2885)
Browse files Browse the repository at this point in the history
* restore cursor pos after formatting

* remove a todo:
  • Loading branch information
devoncarew authored Mar 7, 2024
1 parent cab8af0 commit b2594ea
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 18 deletions.
17 changes: 15 additions & 2 deletions pkgs/sketch_pad/lib/editor/codemirror.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,15 @@ extension type CodeMirror._(JSObject _) implements JSObject {
external void refresh();
external void focus();
external void showHint(HintOptions? options);
external JSAny execCommand(String command, [JSAny? object]);
external JSAny? execCommand(String command);
external void on(String event, JSFunction callback);

String getTheme() => (getOption('theme') as JSString).toDart;
void setTheme(String theme) => setOption('theme', theme.toJS);

external void scrollTo(num? x, num? y);
external ScrollInfo getScrollInfo();

void setReadOnly(bool value, [bool noCursor = false]) {
if (value) {
if (noCursor) {
Expand Down Expand Up @@ -78,7 +81,7 @@ extension type Doc._(JSObject _) implements JSObject {
external String getValue();
external String? getLine(int n);
external bool somethingSelected();
external String? getSelection(String s);
external String? getSelection(String? s);
external void setSelection(Position position, [Position head]);
external JSArray<TextMarker> getAllMarks();
external TextMarker markText(
Expand All @@ -89,6 +92,16 @@ extension type Doc._(JSObject _) implements JSObject {
external Position posFromIndex(int index);
}

@anonymous
extension type ScrollInfo._(JSObject _) implements JSObject {
external int top;
external int left;
external int width;
external int height;
external int clientWidth;
external int clientHeight;
}

@anonymous
extension type Position._(JSObject _) implements JSObject {
external int line;
Expand Down
42 changes: 36 additions & 6 deletions pkgs/sketch_pad/lib/editor/editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ import '../embed.dart';
import '../model.dart';
import 'codemirror.dart';

// TODO: show documentation on hover

// TODO: implement find / find next

// TODO: improve the code completion UI

// TODO: hover - show links to hosted dartdoc? (flutter, dart api, packages)

const String _viewType = 'dartpad-editor';

bool _viewFactoryInitialized = false;
Expand Down Expand Up @@ -120,6 +128,19 @@ class _EditorWidgetState extends State<EditorWidget> implements EditorService {
codeMirror?.focus();
}

@override
int get cursorOffset {
final pos = codeMirror?.getCursor();
if (pos == null) return 0;

return codeMirror?.getDoc().indexFromPos(pos) ?? 0;
}

@override
void focus() {
codeMirror?.focus();
}

@override
void initState() {
super.initState();
Expand Down Expand Up @@ -231,8 +252,19 @@ class _EditorWidgetState extends State<EditorWidget> implements EditorService {
}

void _updateCodemirrorFromModel() {
final value = widget.appModel.sourceCodeController.text;
codeMirror?.getDoc().setValue(value);
final value = widget.appModel.sourceCodeController.value;
final cursorOffset = value.selection.baseOffset;
final cm = codeMirror!;
final doc = cm.getDoc();

if (cursorOffset == -1) {
doc.setValue(value.text);
} else {
final scrollInfo = cm.getScrollInfo();
doc.setValue(value.text);
doc.setSelection(doc.posFromIndex(cursorOffset));
cm.scrollTo(scrollInfo.left, scrollInfo.top);
}
}

void _updateEditableStatus() {
Expand Down Expand Up @@ -324,10 +356,8 @@ class _EditorWidgetState extends State<EditorWidget> implements EditorService {
// codemirror commands

JSAny? _handleGoLineLeft(CodeMirror editor) {
// Change the cmd-left behavior to move the cursor to the leftmost non-ws
// char.
editor.execCommand('goLineLeftSmart');
return JSObject();
// Change the cmd-left behavior to move the cursor to leftmost non-ws char.
return editor.execCommand('goLineLeftSmart');
}

void _indentIfMultiLineSelectionElseInsertSoftTab(CodeMirror editor) {
Expand Down
13 changes: 7 additions & 6 deletions pkgs/sketch_pad/lib/execution/frame_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ import 'dart:js_interop_unsafe';

import 'package:web/web.dart';

/// Extensions to work around Dart compiler issues that result
/// in calls that sandboxed iframes error on.
/// Extensions to work around Dart compiler issues that result in calls that
/// sandboxed iframes error on.
///
/// If the compilers are adjusted to handle this case or `package:web` provides
/// a helper for this, switch to that.
///
/// If the compilers are adjusted to handle this case or
/// `package:web` provides a helper for this, switch to that.
/// Tracked in https://github.com/dart-lang/sdk/issues/54443.
extension HTMLIFrameElementExtension on HTMLIFrameElement {
/// Send the specified [message] to this iframe,
/// configured with the specified [optionsOrTargetOrigin].
/// Send the specified [message] to this iframe, configured with the specified
/// [optionsOrTargetOrigin].
void safelyPostMessage(
JSAny? message,
String optionsOrTargetOrigin,
Expand Down
16 changes: 12 additions & 4 deletions pkgs/sketch_pad/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -470,15 +470,23 @@ class _DartPadMainPageState extends State<DartPadMainPage>

Future<void> _handleFormatting() async {
try {
final value = appModel.sourceCodeController.text;
final result = await appServices.format(SourceRequest(source: value));
final source = appModel.sourceCodeController.text;
final offset = appServices.editorService?.cursorOffset;
final result = await appServices.format(
SourceRequest(source: source, offset: offset),
);

if (result.source == value) {
if (result.source == source) {
appModel.editorStatus.showToast('No formatting changes');
} else {
appModel.editorStatus.showToast('Format successful');
appModel.sourceCodeController.text = result.source;
appModel.sourceCodeController.value = TextEditingValue(
text: result.source,
selection: TextSelection.collapsed(offset: result.offset ?? 0),
);
}

appServices.editorService!.focus();
} catch (error) {
appModel.editorStatus.showToast('Error formatting code');
appModel.appendLineToConsole('Formatting issue: $error');
Expand Down
2 changes: 2 additions & 0 deletions pkgs/sketch_pad/lib/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ abstract class EditorService {
void showCompletions();
void showQuickFixes();
void jumpTo(AnalysisIssue issue);
int get cursorOffset;
void focus();
}

class AppModel {
Expand Down

0 comments on commit b2594ea

Please sign in to comment.