// Licensed to Cloudera, Inc. under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. Cloudera, Inc. licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import { EXECUTABLE_UPDATED_TOPIC, ExecutableUpdatedEvent } from '../../../../apps/editor/execution/events'; import { Ace } from '../../../../ext/ace'; import AceAnchoredRange from './AceAnchoredRange'; import { ACTIVE_STATEMENT_CHANGED_EVENT } from './AceLocationHandler'; import Executor from '../../../../apps/editor/execution/executor'; import SqlExecutable from '../../../../apps/editor/execution/sqlExecutable'; import SubscriptionTracker, { Disposable } from '../../../../components/utils/SubscriptionTracker'; import { ParsedSqlStatement } from '../../../../parse/sqlStatementsParser'; const LINE_BREAK_REGEX = /(\r\n)|(\n)|(\r)/g; const LEADING_WHITE_SPACE_REGEX = /^\s+/; const ACTIVE_CSS = 'ace-active-gutter-decoration'; const COMPLETED_CSS = 'ace-completed-gutter-decoration'; const EXECUTING_CSS = 'ace-executing-gutter-decoration'; const FAILED_CSS = 'ace-failed-gutter-decoration'; const FAILED_MARKER_CSS = 'ace-failed-marker'; const getLeadingEmptyLineCount = (parsedStatement: ParsedSqlStatement): number => { let leadingEmptyLineCount = 0; const leadingWhiteSpace = parsedStatement.statement.match(LEADING_WHITE_SPACE_REGEX); if (leadingWhiteSpace) { const lineBreakMatch = leadingWhiteSpace[0].match(LINE_BREAK_REGEX); if (lineBreakMatch) { leadingEmptyLineCount = lineBreakMatch.length; } } return leadingEmptyLineCount; }; export default class AceGutterHandler implements Disposable { editor: Ace.Editor; editorId: string; executor: Executor; trackedAnchors: Map = new Map(); subTracker: SubscriptionTracker = new SubscriptionTracker(); constructor(options: { editor: Ace.Editor; editorId: string; executor: Executor }) { this.editor = options.editor; this.editorId = options.editorId; this.executor = options.executor; const activeStatementAnchor = new AceAnchoredRange(this.editor); activeStatementAnchor.setGutterCss(ACTIVE_CSS); this.subTracker.subscribe(ACTIVE_STATEMENT_CHANGED_EVENT, statementDetails => { if (statementDetails.id !== this.editorId || !statementDetails.activeStatement) { return; } const leadingEmptyLineCount = getLeadingEmptyLineCount(statementDetails.activeStatement); activeStatementAnchor.move(statementDetails.activeStatement.location, leadingEmptyLineCount); }); this.subTracker.addDisposable(activeStatementAnchor); if (this.executor) { this.subTracker.subscribe(EXECUTABLE_UPDATED_TOPIC, executable => { if (executable.executor === this.executor) { let anchor = this.trackedAnchors.get(executable.id); if (!anchor) { anchor = new AceAnchoredRange(this.editor); this.trackedAnchors.set(executable.id, anchor); } if (executable.lost) { anchor.dispose(); this.trackedAnchors.delete(executable.id); return; } anchor.removeGutterCss(); anchor.removeMarkerRowCss(); const statement = (executable as SqlExecutable).parsedStatement; const leadingEmptyLineCount = getLeadingEmptyLineCount(statement); anchor.move(statement.location, leadingEmptyLineCount); if (executable.isRunning()) { anchor.setGutterCss(EXECUTING_CSS); } else if (!executable.edited && executable.isSuccess()) { anchor.setGutterCss(COMPLETED_CSS); } else if (!executable.edited && executable.isFailed()) { anchor.setGutterCss(FAILED_CSS); if (executable.logs && executable.logs.errors.length) { const error = executable.logs.errors[0]; anchor.setMarkerRowCss(FAILED_MARKER_CSS, error.row - leadingEmptyLineCount - 1); } } } }); } } dispose(): void { this.subTracker.dispose(); } }