@kui-shell/plugin-git
Version:
Kui plugin that offers git integrations
176 lines • 7.76 kB
JavaScript
/*
* Copyright 2020 The Kubernetes Authors
*
* Licensed 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.
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import React from 'react';
import { basename, dirname, join } from 'path';
import Icons from '@kui-shell/plugin-client-common/mdist/components/spi/Icons';
import TextWithIconWidget from '@kui-shell/plugin-client-common/mdist/components/Client/StatusStripe/TextWithIconWidget';
import { Capabilities, Events, getCurrentTab, i18n, encodeComponent, pexecInCurrentTab } from '@kui-shell/core';
const strings = i18n('plugin-bash-like');
const strings2 = i18n('plugin-git');
export default class CurrentGitBranch extends React.PureComponent {
constructor(props) {
super(props);
this.handler = this.reportCurrentBranch.bind(this);
/** So we don't handle events after unmounting */
this._unmounted = true;
this.state = {
text: '',
status: '',
viewLevel: 'hidden'
};
}
get unmounted() {
return this._unmounted;
}
set unmounted(umm) {
this._unmounted = umm;
}
debounce() {
const now = Date.now();
const last = this.last;
this.last = now;
return last && now - last < 250;
}
/**
* Check the current branch, and the dirtiness thereof.
*
*/
reportCurrentBranch() {
return __awaiter(this, void 0, void 0, function* () {
if (this.unmounted) {
return;
}
const tab = getCurrentTab();
if (!tab || !tab.REPL) {
return;
}
else if (this.debounce()) {
return;
}
try {
const [isDirty, branch] = yield Promise.all([
// exit 0/1 indicates clean/dirty
tab.REPL.qexec('git diff-index --quiet HEAD --')
.then(() => false)
.catch(() => true),
// exits with branch name
tab.REPL.qexec('git rev-parse --abbrev-ref HEAD')
]);
tab.REPL.qexec('git status -s').then(status => this.setState({ status: status === true ? '' : status }));
// is the branch dirty?
this.setState({
text: branch,
viewLevel: isDirty ? 'warn' : 'normal'
});
}
catch (error) {
const err = error;
this.last = undefined;
if (err.code !== 128 && !/ambiguous argument 'HEAD'/.test(err.message) && !/not a git repo/.test(err.message)) {
// 128: not a git repository; don't report those as errors
console.error('unable to determine git branch', err.code, typeof err.code, err);
}
// but, in either case, hide the entry
this.setState({
text: strings('not a repo'),
viewLevel: 'hidden'
});
}
});
}
/**
* Once we have mounted, we immediately check the current branch,
* and schedule an update based on standard REPL events.
*
*/
componentDidMount() {
this.unmounted = false;
if (Capabilities.inBrowser()) {
Events.eventBus.once('/tab/new', this.handler);
}
else {
this.handler();
}
Events.wireToStandardEvents(this.handler);
}
/** Make sure to unsubscribe! */
componentWillUnmount() {
this.unmounted = true;
Events.unwireToStandardEvents(this.handler);
}
/** @return the header for the Popover component */
popoverHeader() {
return (React.createElement(React.Fragment, null,
React.createElement("div", null, strings('Git Branch')),
React.createElement("div", null,
React.createElement("strong", null, this.state.text)),
React.createElement("div", { className: "sub-text even-smaller-text" }, this.changeBranch())));
}
/** @return the body for the Popover component */
popoverBody() {
const statusModel = this.statusModel();
return (React.createElement("div", { className: "top-pad bottom-pad" },
this.summary(statusModel),
this.changes(statusModel)));
}
/** @return a model of `git status -s` */
statusModel() {
return !this.state.status
? []
: this.state.status
.split(/\n/)
.map(line => line.match(/(.+)\s+(.+)/))
.filter(Boolean)
.map(match => ({ M: match[1], file: match[2] }));
}
/** @return UI that summarizes the `statusModel` changes */
summary(statusModel) {
return (React.createElement("span", { className: "sub-text" }, statusModel.length > 0
? strings('You have made the following changes to this branch.')
: strings('You have made no changes to this branch.')));
}
/** @return UI for changes represented by `statusModel` */
changes(statusModel) {
return (React.createElement("div", { className: "small-top-pad monospace even-smaller-text pre-wrap" }, statusModel.map(({ M, file }) => (React.createElement("div", { key: `${M}-${file}`, className: "tiny-top-pad" },
[...M].map((m, idx) => (React.createElement("strong", { key: `${m}-${idx}`, className: /M/.test(m) ? 'red-text' : /D/.test(m) ? 'red-text' : /A/.test(m) ? 'cyan-text' : '' }, m))),
React.createElement("span", { title: file, className: "small-left-pad clickable", onClick: () => pexecInCurrentTab(`git diff ${encodeComponent(file)}`) }, join(basename(dirname(file)), basename(file))))))));
}
/** @return desired Popover model */
popover() {
return {
bodyContent: this.popoverBody(),
headerContent: this.popoverHeader()
};
}
/** @return UI for changing branches */
changeBranch() {
return (React.createElement("a", { href: "#", onClick: () => pexecInCurrentTab('git branch') }, strings2('Switch branch')));
}
render() {
return (React.createElement(TextWithIconWidget, { className: this.props.className, text: this.state.text, viewLevel: this.state.viewLevel, id: "kui--plugin-git--current-git-branch", title: strings2('Your current git branch'), popover: this.popover() },
React.createElement(Icons, { icon: "CodeBranch" })));
}
}
//# sourceMappingURL=CurrentGitBranch.js.map