Add Icon / HotKey to Delete Line (Cmd-D) (#214)

* work on cmd-d to delete a selected line

* call stoppropagation when code.tsx captures keyboard input

* finish up with line delete.  add a trash icon to line.  prevent delete when cmd is running (show error msg)
This commit is contained in:
Mike Sawka 2024-01-08 22:58:32 -08:00 committed by GitHub
parent f6a6068674
commit 8ac1943d56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 99 additions and 18 deletions

View File

@ -596,6 +596,10 @@
gap: 4px;
align-self: stretch;
width: 100%;
.settings-input .hotkey {
color: @text-secondary;
}
}
}
}

View File

@ -17,7 +17,16 @@ import {
Screen,
Session,
} from "../../../model/model";
import { Toggle, InlineSettingsTextEdit, SettingsError, InfoMessage, Modal, Dropdown, Tooltip } from "../common";
import {
Toggle,
InlineSettingsTextEdit,
SettingsError,
InfoMessage,
Modal,
Dropdown,
Tooltip,
Button,
} from "../common";
import { LineType, RendererPluginType, ClientDataType, CommandRtnType, RemoteType } from "../../../types/types";
import { PluginModel } from "../../../plugins/plugins";
import * as util from "../../../util/util";
@ -632,12 +641,6 @@ class LineSettingsModal extends React.Component<{}, {}> {
/>
</div>
</div>
<div className="settings-field">
<div className="settings-label">Archived</div>
<div className="settings-input">
<Toggle checked={!!line.archived} onChange={this.handleChangeArchived} />
</div>
</div>
<SettingsError errorMessage={this.errorMessage} />
<div style={{ height: 50 }} />
</div>

View File

@ -354,6 +354,12 @@ class LineCmd extends React.Component<
GlobalCommandRunner.lineBookmark(line.lineid);
}
@boundMethod
clickDelete() {
let { line } = this.props;
GlobalCommandRunner.lineDelete(line.lineid, true);
}
@boundMethod
clickMinimize() {
mobx.action(() => {
@ -659,6 +665,9 @@ class LineCmd extends React.Component<
{this.renderMeta1(cmd)}
<If condition={!hidePrompt}>{this.renderCmdText(cmd)}</If>
</div>
<div key="delete" title="Delete Line (&#x2318;D)" className="line-icon" onClick={this.clickDelete}>
<i className="fa-sharp fa-regular fa-trash" />
</div>
<div
key="bookmark"
title="Bookmark"

View File

@ -3365,7 +3365,7 @@ class Model {
// nothing for now
}
docKeyDownHandler(e: any) {
docKeyDownHandler(e: KeyboardEvent) {
if (isModKeyPress(e)) {
return;
}
@ -3427,6 +3427,37 @@ class Model {
}
}
}
if (e.code == "KeyD" && e.getModifierState("Meta")) {
let ranDelete = this.deleteActiveLine();
if (ranDelete) {
e.preventDefault();
}
}
}
deleteActiveLine(): boolean {
let activeScreen = this.getActiveScreen();
if (activeScreen == null || activeScreen.getFocusType() != "cmd") {
return false;
}
let selectedLine = activeScreen.selectedLine.get();
if (selectedLine == null || selectedLine <= 0) {
return false;
}
let line = activeScreen.getLineByNum(selectedLine);
if (line == null) {
return false;
}
let cmd = activeScreen.getCmd(line);
if (cmd != null) {
if (cmd.isRunning()) {
let info: T.InfoType = { infomsg: "Cannot delete a running command" };
this.inputModel.flashInfoMsg(info, 2000);
return false;
}
}
GlobalCommandRunner.lineDelete(String(selectedLine), true);
return true;
}
clearModals(): boolean {
@ -4311,6 +4342,10 @@ class CommandRunner {
return GlobalModel.submitCommand("line", "archive", [lineArg, archiveStr], kwargs, false);
}
lineDelete(lineArg: string, interactive: boolean): Promise<CommandRtnType> {
return GlobalModel.submitCommand("line", "delete", [lineArg], { nohist: "1" }, interactive);
}
lineSet(lineArg: string, opts: { renderer?: string }): Promise<CommandRtnType> {
let kwargs = { nohist: "1" };
if ("renderer" in opts) {

View File

@ -3,7 +3,8 @@
import * as React from "react";
import * as T from "../../types/types";
import Editor from "@monaco-editor/react";
import Editor, { Monaco } from "@monaco-editor/react";
import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api";
import { Markdown } from "../../app/common/common";
import { GlobalModel, GlobalCommandRunner } from "../../model/model";
import Split from "react-split-it";
@ -146,21 +147,24 @@ class SourceCodeRenderer extends React.Component<
}
};
handleEditorDidMount = (editor, monaco) => {
handleEditorDidMount = (editor: MonacoTypes.editor.IStandaloneCodeEditor, monaco: Monaco) => {
this.monacoEditor = editor;
this.setInitialLanguage(editor);
this.setEditorHeight();
editor.onKeyDown((e) => {
if (e.code === "KeyS" && (e.ctrlKey || e.metaKey) && this.state.isSave) {
editor.onKeyDown((e: MonacoTypes.IKeyboardEvent) => {
if (e.code === "KeyS" && e.metaKey && this.state.isSave) {
e.preventDefault();
e.stopPropagation();
this.doSave();
}
if (e.code === "KeyD" && (e.ctrlKey || e.metaKey)) {
if (e.code === "KeyD" && e.metaKey) {
e.preventDefault();
e.stopPropagation();
this.doClose();
}
if (e.code === "KeyP" && (e.ctrlKey || e.metaKey)) {
if (e.code === "KeyP" && e.metaKey) {
e.preventDefault();
e.stopPropagation();
this.togglePreview();
}
});

View File

@ -3440,7 +3440,7 @@ func LineDeleteCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (s
}
err = sstore.DeleteLinesByIds(ctx, ids.ScreenId, lineIds)
if err != nil {
return nil, fmt.Errorf("/line:delete error purging lines: %v", err)
return nil, fmt.Errorf("/line:delete error deleting lines: %v", err)
}
update := &sstore.ModelUpdate{}
for _, lineId := range lineIds {
@ -3451,6 +3451,11 @@ func LineDeleteCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (s
}
update.Lines = append(update.Lines, lineObj)
}
screen, err := sstore.FixupScreenSelectedLine(ctx, ids.ScreenId)
if err != nil {
return nil, fmt.Errorf("/line:delete error fixing up screen: %v", err)
}
update.Screens = []*sstore.ScreenType{screen}
return update, nil
}

View File

@ -2039,6 +2039,29 @@ func SetLineArchivedById(ctx context.Context, screenId string, lineId string, ar
return txErr
}
// returns updated screen (only if updated)
func FixupScreenSelectedLine(ctx context.Context, screenId string) (*ScreenType, error) {
return WithTxRtn(ctx, func(tx *TxWrap) (*ScreenType, error) {
query := `SELECT selectedline FROM screen WHERE screenid = ?`
sline := tx.GetInt(query, screenId)
query = `SELECT linenum FROM line WHERE screenid = ? AND linenum = ?`
if tx.Exists(query, screenId, sline) {
// selected line is valid
return nil, nil
}
query = `SELECT min(linenum) FROM line WHERE screenid = ? AND linenum > ?`
newSLine := tx.GetInt(query, screenId, sline)
if newSLine == 0 {
query = `SELECT max(linenum) FROM line WHERE screenid = ? AND linenum < ?`
newSLine = tx.GetInt(query, screenId, sline)
}
// newSLine might be 0, but that's ok (because that means there are no lines)
query = `UPDATE screen SET selectedline = ? WHERE screenid = ?`
tx.Exec(query, newSLine, screenId)
return GetScreenById(tx.Context(), screenId)
})
}
func DeleteLinesByIds(ctx context.Context, screenId string, lineIds []string) error {
txErr := WithTx(ctx, func(tx *TxWrap) error {
isWS := isWebShare(tx, screenId)
@ -2046,9 +2069,8 @@ func DeleteLinesByIds(ctx context.Context, screenId string, lineIds []string) er
query := `SELECT status FROM cmd WHERE screenid = ? AND lineid = ?`
cmdStatus := tx.GetString(query, screenId, lineId)
if cmdStatus == CmdStatusRunning {
return fmt.Errorf("cannot delete line[%s:%s], cmd is running", screenId, lineId)
return fmt.Errorf("cannot delete line[%s], cmd is running", lineId)
}
query = `DELETE FROM line WHERE screenid = ? AND lineid = ?`
tx.Exec(query, screenId, lineId)
query = `DELETE FROM cmd WHERE screenid = ? AND lineid = ?`
@ -2056,7 +2078,6 @@ func DeleteLinesByIds(ctx context.Context, screenId string, lineIds []string) er
// don't delete history anymore, just remove lineid reference
query = `UPDATE history SET lineid = '', linenum = 0 WHERE screenid = ? AND lineid = ?`
tx.Exec(query, screenId, lineId)
if isWS {
insertScreenLineUpdate(tx, screenId, lineId, UpdateType_LineDel)
}