mirror of https://github.com/onivim/oni.git
Feature/initial Git VCS provider (#1310)
* add git addition and deletion count * add typing to git service * add ideas for styling * add error handling and checks to determine rendering * reorder comma separator * get gitroot utility and pass in current dit * fix name typo for gitroot fn * add logs for debugging * [WIP] debug ocaml lang server * promisify exec command merge upstream * add semicolon to external commandline config * add semicolon to js file as per lint error * write manual diffSummary function add per file changes and option to show perfile changes * add try catch block to prevent errors * add further check for error points * comment out unused fn call cast promisified type to any add fixme comment to check back in re this typing * add FA icons add spacer char change rendering so elements are only created as necessary * change long line initialisation to explicit decls * typos re variable names * revert markdown changes * revert markdown changes * remove local changes as they make little sense fix counting of local changes in case of future use case * re-add git-js * tweak use of git js * initialise git sync then use return git obj * remove cwd calls from git instantiation * ongoing experimentations with git plugin * initial re-implementation of git vcs as per #2110 * attempt promises with git and use workspace * use simple-git to get branch * remove console.log return empty string if no current branch * try using require version of simple git and saving git instance * try using chaining api rather than async/await * add git branch menu and tidy up plugin add get branches method and git checkout method to vcs provider * move git into version control service and add index * Use arrow functions in git vcs provider * add simple git to webpack external to prevent minfication mangling * add fetch branch commands * invert git plugin and git provider [WIP] * add functioning vcs manager * separate components into separate file remove specific references to git * add vcs components file * remove errant console.log * switch to using the registerProvider pattern * add sidebar plugin for vcs * tweak title styles and add modified count * use npm/yarn-run-all for start command to avoid memory leak * fix typing of files in vcsStore * use oni.log to log errors or default to console * revert changes in README.md * add simple-git as a dependency and typescript as a dev dependency * use run-s for unit tests * format response from git js to match VCSProvider return status object and render based on that * add more events to VersionControl interface * add enzyme tests for components, refactor additions and completions * add enzyme test file * tweak styles following component refactor * tidy up deletions and insertions component * add tests for VersionControlView component * fix stagedfileschanged event name typo use events to trigger getStatus in vcs pane * encapsulate getInstance phew.. really bugged me trade accuracy for cleverness in update branch indicator * fix use of menu api as it miss matches with the menumanager * dont export the init function in vcsmanager * Set error state in redux on error * update rxjs to 5.5.10 to match oni-types package * use fork of "oni-type" to test if ci passes * fix overflow issues and use word-break * wrap provider methods in try catch and explicitly return void or val * downgrade to rxjs 5.5.8 * revert to now fixed oni-types package * add initial functionality to activate and deactivate plugin * [WIP] Switch to activation and deactivation exclusively through events * remove console.log * add check inside vcsManager to control when plugin loads WIP * fix provider selection check * remove extra newline * upgrade oni types to 0.0.8 * fix manager comment typo add, ability to navigate titles add visibility filter to view component remove unused check in get status * add ability to collapse and expand sections * update tests and change from using capitalize fn :( * Move version control components, tidy up provider activations handler fix tests and componentize the caret * update snapshots * fix parsing issues in ui-test tabs * revert changes in FileConfiguration and tsconfig react * only create sidebar if none is existent remove unnecessary return, revert prettier to master version * remove useless wrapper div * update snapshots * allow git provider errors to bubble up so they can be handled with notifications which inform the user of why something is not working * remove unused var * add vcsmanager test in jest * fix not re-applying state error * pull upstream * remove uses of Log and use oni-core-logging * merge upstream fix conflicts * fix vcs name issue re. notifications * remove extra variable in App.ts * set simple git working dir constructor and handle updates there remove explicit passing in of working dir * remove vcs provider from duplicate interface * update functions to remove unnecessary args * run get diff and get branch in parallel fix missing return in diff fn * explicitly pass in the project root to prevent race conditions whilst changing dir * fix removal of initial gitp assignment in constructor * improve version control manager tests * update jest to avoid coverage bug improve test further * add send notification to vcs pane args * update snapshots * fix import ordering in view * gst * separate out subscription creation and error notifications switch package json plugin commands to && chain * remove console.logs * rename args * split out mocks into separate files * update tests wip * remove is repo check since plugin should only activate if is true convert handle provider to async function * await handle provider status * remove console.log assign subscriptions directly * upgrade ts-jest and redux mock store * add readable names to handle provider status * import * for vcs types fix import of redux mock store * add vcs definition file to be deleted once oni-api adds vcs * initialise project root to activeWorkspace * revert changes to commont.ts use SFC typing * fix package json * add vcs store test * fix new name of versioncontrolstate * hide sidebar behind experimental feature flag * remove collect coverage from jest config option * update version control pane to focus on testing react not redux * add jest to codecov command remove unnecessary type annotations * add jest coverage json to final istanbul report * update jest coverage runner * change path name for jest coverage file * add inlineSourceMap option to jest tsconfig * remove jest coverage merging * add jest coverage upload command * remove unnecessary coverage flag and type annotations * fix chained and operator ui bug using thing && thing && component can cause 0 to be rendered
This commit is contained in:
parent
388c740aae
commit
97899e1222
|
@ -89,6 +89,7 @@ export const start = async (args: string[]): Promise<void> => {
|
|||
const globalCommandsPromise = import("./Services/Commands/GlobalCommands")
|
||||
const inputManagerPromise = import("./Services/InputManager")
|
||||
const languageManagerPromise = import("./Services/Language")
|
||||
const vcsManagerPromise = import("./Services/VersionControl")
|
||||
const notificationsPromise = import("./Services/Notifications")
|
||||
const snippetPromise = import("./Services/Snippets")
|
||||
const keyDisplayerPromise = import("./Services/KeyDisplayer")
|
||||
|
@ -235,9 +236,9 @@ export const start = async (args: string[]): Promise<void> => {
|
|||
|
||||
const Notifications = await notificationsPromise
|
||||
Notifications.activate(configuration, overlayManager)
|
||||
const notifications = Notifications.getInstance()
|
||||
|
||||
if (typeof developmentPluginError !== "undefined") {
|
||||
const notifications = Notifications.getInstance()
|
||||
const notification = notifications.createItem()
|
||||
notification.setContents(developmentPluginError.title, developmentPluginError.errorText)
|
||||
notification.setLevel("error")
|
||||
|
@ -248,7 +249,6 @@ export const start = async (args: string[]): Promise<void> => {
|
|||
}
|
||||
|
||||
configuration.onConfigurationError.subscribe(err => {
|
||||
const notifications = Notifications.getInstance()
|
||||
const notification = notifications.createItem()
|
||||
notification.setContents("Error Loading Configuration", err.toString())
|
||||
notification.setLevel("error")
|
||||
|
@ -318,6 +318,18 @@ export const start = async (args: string[]): Promise<void> => {
|
|||
Sidebar.activate(configuration, workspace)
|
||||
const sidebarManager = Sidebar.getInstance()
|
||||
|
||||
const VCSManager = await vcsManagerPromise
|
||||
VCSManager.activate(
|
||||
workspace,
|
||||
editorManager,
|
||||
statusBar,
|
||||
commandManager,
|
||||
menuManager,
|
||||
sidebarManager,
|
||||
notifications,
|
||||
configuration,
|
||||
)
|
||||
|
||||
Explorer.activate(
|
||||
commandManager,
|
||||
configuration,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as Git from "../../Services/Git"
|
||||
import { getInstance, VersionControlManager } from "./../../Services/VersionControl"
|
||||
|
||||
export class Services {
|
||||
public get git(): any {
|
||||
return Git
|
||||
public get vcs(): VersionControlManager {
|
||||
return getInstance()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as fs from "fs"
|
|||
import * as path from "path"
|
||||
|
||||
import * as Oni from "oni-api"
|
||||
import { Event, IEvent } from "oni-types"
|
||||
|
||||
import { Configuration, getUserConfigFolderPath } from "./../Services/Configuration"
|
||||
import { IContributions } from "./Api/Capabilities"
|
||||
|
@ -23,6 +24,11 @@ export class PluginManager implements Oni.IPluginManager {
|
|||
private _anonymousPlugin: AnonymousPlugin
|
||||
private _pluginsActivated: boolean = false
|
||||
private _installer: IPluginInstaller = new YarnPluginInstaller()
|
||||
private _pluginsLoaded = new Event<void>()
|
||||
|
||||
public get pluginsAllLoaded(): IEvent<void> {
|
||||
return this._pluginsLoaded
|
||||
}
|
||||
|
||||
private _developmentPluginsPath: string[] = []
|
||||
|
||||
|
@ -81,6 +87,7 @@ export class PluginManager implements Oni.IPluginManager {
|
|||
})
|
||||
|
||||
this._pluginsActivated = true
|
||||
this._pluginsLoaded.dispatch()
|
||||
|
||||
return this._anonymousPlugin.oni
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ const BaseConfiguration: IConfigurationValues = {
|
|||
"wildmenu.mode": true,
|
||||
"commandline.mode": true,
|
||||
"commandline.icons": true,
|
||||
"experimental.vcs.sidebar": false,
|
||||
"experimental.particles.enabled": false,
|
||||
"experimental.preview.enabled": false,
|
||||
"experimental.welcome.enabled": false,
|
||||
|
|
|
@ -49,6 +49,8 @@ export interface IConfigurationValues {
|
|||
// Whether or not the learning pane is available
|
||||
"experimental.particles.enabled": boolean
|
||||
|
||||
// Whether Version control sidebar item is enabled
|
||||
"experimental.vcs.sidebar": boolean
|
||||
// Whether the color highlight layer is enabled
|
||||
"experimental.colorHighlight.enabled": boolean
|
||||
// Whitelist of extension for the color highlight layer
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/**
|
||||
* Git.ts
|
||||
*
|
||||
* Utilities around Git
|
||||
*/
|
||||
|
||||
import { exec } from "child_process"
|
||||
|
||||
interface IExecOptions {
|
||||
cwd?: string
|
||||
}
|
||||
|
||||
export function getBranch(path?: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options: IExecOptions = {}
|
||||
if (path) {
|
||||
options.cwd = path
|
||||
}
|
||||
|
||||
exec(
|
||||
"git rev-parse --abbrev-ref HEAD",
|
||||
options,
|
||||
(error: any, stdout: string, stderr: string) => {
|
||||
if (error && error.code) {
|
||||
reject(new Error(stderr))
|
||||
} else {
|
||||
resolve(stdout)
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
|
@ -95,7 +95,8 @@ export const start = (configuration: Configuration, notifications: Notifications
|
|||
const errorText = val ? val.toString() : "Open the debugger for more details."
|
||||
showError(
|
||||
"Unhandled Exception",
|
||||
errorText + "\nPlease report this error. Callstack: " + val.stack,
|
||||
errorText +
|
||||
`\nPlease report this error. ${val && val.stack ? `Callstack:` + val.stack : ""}`,
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -0,0 +1,309 @@
|
|||
import { capitalize } from "lodash"
|
||||
import * as Oni from "oni-api"
|
||||
import * as Log from "oni-core-logging"
|
||||
import { IDisposable } from "oni-types"
|
||||
import * as React from "react"
|
||||
|
||||
import { store, SupportedProviders, VersionControlPane, VersionControlProvider } from "./"
|
||||
import { Notifications } from "./../../Services/Notifications"
|
||||
import { Branch } from "./../../UI/components/VersionControl"
|
||||
import { MenuManager } from "./../Menu"
|
||||
import { SidebarManager } from "./../Sidebar"
|
||||
import { IWorkspace } from "./../Workspace"
|
||||
|
||||
interface ISendNotificationsArgs {
|
||||
detail: string
|
||||
level: "info" | "warn"
|
||||
title: string
|
||||
}
|
||||
|
||||
export type ISendVCSNotification = (args: ISendNotificationsArgs) => void
|
||||
|
||||
export class VersionControlManager {
|
||||
private _vcs: SupportedProviders
|
||||
private _vcsProvider: VersionControlProvider
|
||||
private _menuInstance: Oni.Menu.MenuInstance
|
||||
private _vcsStatusItem: Oni.StatusBarItem
|
||||
private _subscriptions: IDisposable[] = []
|
||||
private _providers = new Map<string, VersionControlProvider>()
|
||||
|
||||
constructor(
|
||||
private _workspace: IWorkspace,
|
||||
private _editorManager: Oni.EditorManager,
|
||||
private _statusBar: Oni.StatusBar,
|
||||
private _menu: MenuManager,
|
||||
private _commands: Oni.Commands.Api,
|
||||
private _sidebar: SidebarManager,
|
||||
private _notifications: Notifications,
|
||||
private _configuration: Oni.Configuration,
|
||||
) {}
|
||||
|
||||
public get providers() {
|
||||
return this._providers
|
||||
}
|
||||
|
||||
public get activeProvider(): VersionControlProvider {
|
||||
return this._vcsProvider
|
||||
}
|
||||
|
||||
public async registerProvider(provider: VersionControlProvider): Promise<void> {
|
||||
if (provider) {
|
||||
this._providers.set(provider.name, provider)
|
||||
const canHandleWorkspace = await provider.canHandleWorkspace()
|
||||
if (canHandleWorkspace) {
|
||||
await this._activateVCSProvider(provider)
|
||||
}
|
||||
|
||||
this._workspace.onDirectoryChanged.subscribe(async dir => {
|
||||
const providerToUse = await this.getCompatibleProvider(dir)
|
||||
await this.handleProviderStatus(providerToUse)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Use arrow function to maintain this binding of sendNotification
|
||||
public sendNotification: ISendVCSNotification = ({ detail, level, title }) => {
|
||||
const notification = this._notifications.createItem()
|
||||
notification.setContents(title, detail)
|
||||
notification.setExpiration(3_000)
|
||||
notification.setLevel(level)
|
||||
notification.show()
|
||||
}
|
||||
|
||||
public deactivateProvider(): void {
|
||||
this._vcsProvider.deactivate()
|
||||
this._subscriptions.map(s => s.dispose())
|
||||
if (this._vcsStatusItem) {
|
||||
this._vcsStatusItem.hide()
|
||||
}
|
||||
this._vcsProvider = null
|
||||
this._vcs = null
|
||||
}
|
||||
|
||||
public async handleProviderStatus(newProvider: VersionControlProvider): Promise<void> {
|
||||
const isSameProvider = this._vcsProvider && newProvider && this._vcs === newProvider.name
|
||||
const noCompatibleProvider = this._vcsProvider && !newProvider
|
||||
const newReplacementProvider = Boolean(this._vcsProvider && newProvider)
|
||||
const compatibleProvider = Boolean(!this._vcsProvider && newProvider)
|
||||
|
||||
switch (true) {
|
||||
case isSameProvider:
|
||||
break
|
||||
case noCompatibleProvider:
|
||||
this.deactivateProvider()
|
||||
break
|
||||
case newReplacementProvider:
|
||||
this.deactivateProvider()
|
||||
await this._activateVCSProvider(newProvider)
|
||||
break
|
||||
case compatibleProvider:
|
||||
await this._activateVCSProvider(newProvider)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private async getCompatibleProvider(dir: string): Promise<VersionControlProvider | null> {
|
||||
const allCompatibleProviders: VersionControlProvider[] = []
|
||||
for (const vcs of this._providers.values()) {
|
||||
const isCompatible = await vcs.canHandleWorkspace(dir)
|
||||
if (isCompatible) {
|
||||
allCompatibleProviders.push(vcs)
|
||||
}
|
||||
}
|
||||
// TODO: when we have multiple providers we will need logic to determine which to
|
||||
// use if more than one is compatible
|
||||
const [providerToUse] = allCompatibleProviders
|
||||
|
||||
return providerToUse
|
||||
}
|
||||
|
||||
private _activateVCSProvider = async (provider: VersionControlProvider) => {
|
||||
this._vcs = provider.name
|
||||
this._vcsProvider = provider
|
||||
await this._initialize()
|
||||
provider.activate()
|
||||
}
|
||||
|
||||
private async _initialize() {
|
||||
try {
|
||||
await this._updateBranchIndicator()
|
||||
this._setupSubscriptions()
|
||||
|
||||
const hasVcsSidebar = this._sidebar.entries.some(({ id }) => id.includes("vcs"))
|
||||
const enabled = this._configuration.getValue("experimental.vcs.sidebar")
|
||||
|
||||
if (!hasVcsSidebar && enabled) {
|
||||
const vcsPane = new VersionControlPane(
|
||||
this._editorManager,
|
||||
this._workspace,
|
||||
this._vcsProvider,
|
||||
this.sendNotification,
|
||||
store,
|
||||
)
|
||||
this._sidebar.add("code-fork", vcsPane)
|
||||
}
|
||||
|
||||
this._registerCommands()
|
||||
} catch (e) {
|
||||
Log.warn(`Failed to initialise provider, because, ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
private _setupSubscriptions() {
|
||||
this._subscriptions = [
|
||||
this._editorManager.activeEditor.onBufferEnter.subscribe(async () => {
|
||||
await this._updateBranchIndicator()
|
||||
}),
|
||||
this._vcsProvider.onBranchChanged.subscribe(async newBranch => {
|
||||
await this._updateBranchIndicator(newBranch)
|
||||
await this._editorManager.activeEditor.neovim.command("e!")
|
||||
}),
|
||||
this._editorManager.activeEditor.onBufferSaved.subscribe(async () => {
|
||||
await this._updateBranchIndicator()
|
||||
}),
|
||||
(this._workspace as any).onFocusGained.subscribe(async () => {
|
||||
await this._updateBranchIndicator()
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
private _registerCommands = () => {
|
||||
this._commands.registerCommand({
|
||||
command: `oni.${this._vcs}.fetch`,
|
||||
name: "Fetch the selected branch",
|
||||
detail: "",
|
||||
execute: this._fetchBranch,
|
||||
})
|
||||
|
||||
this._commands.registerCommand({
|
||||
command: `oni.${this._vcs}.branches`,
|
||||
name: `Local ${capitalize(this._vcs)} Branches`,
|
||||
detail: "Open a menu with a list of all local branches",
|
||||
execute: this._createBranchList,
|
||||
})
|
||||
}
|
||||
|
||||
private _updateBranchIndicator = async (branchName?: string) => {
|
||||
if (!this._vcsProvider) {
|
||||
return
|
||||
} else if (!this._vcsStatusItem) {
|
||||
const vcsId = `oni.status.${this._vcs}`
|
||||
this._vcsStatusItem = this._statusBar.createItem(1, vcsId)
|
||||
}
|
||||
|
||||
try {
|
||||
const branch = await this._vcsProvider.getBranch()
|
||||
const diff = await this._vcsProvider.getDiff()
|
||||
|
||||
if (!branch || !diff) {
|
||||
return Log.warn(`The ${!branch ? "branch name" : "diff"} could not be found`)
|
||||
} else if (!branch && !diff) {
|
||||
return this._vcsStatusItem.hide()
|
||||
}
|
||||
|
||||
this._vcsStatusItem.setContents(<Branch branch={branch} diff={diff} />)
|
||||
this._vcsStatusItem.show()
|
||||
} catch (e) {
|
||||
this._notifyOfError(e)
|
||||
return this._vcsStatusItem.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private _createBranchList = async () => {
|
||||
if (!this._vcsProvider) {
|
||||
return
|
||||
}
|
||||
|
||||
const [currentBranch, branches] = await Promise.all([
|
||||
this._vcsProvider.getBranch(),
|
||||
this._vcsProvider.getLocalBranches(),
|
||||
])
|
||||
|
||||
this._menuInstance = this._menu.create()
|
||||
|
||||
if (!branches) {
|
||||
return
|
||||
}
|
||||
|
||||
const branchItems = branches.all.map(branch => ({
|
||||
label: branch,
|
||||
icon: "code-fork",
|
||||
pinned: currentBranch === branch,
|
||||
}))
|
||||
|
||||
this._menuInstance.show()
|
||||
this._menuInstance.setItems(branchItems)
|
||||
|
||||
this._menuInstance.onItemSelected.subscribe(async menuItem => {
|
||||
if (menuItem && menuItem.label) {
|
||||
try {
|
||||
await this._vcsProvider.changeBranch(menuItem.label)
|
||||
} catch (e) {
|
||||
this._notifyOfError(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private _notifyOfError(error: Error) {
|
||||
const name = this._vcsProvider ? capitalize(this._vcs) : "VCS"
|
||||
const errorMessage = error && error.message ? error.message : null
|
||||
this.sendNotification({
|
||||
title: `${capitalize(name)} Plugin Error:`,
|
||||
detail: `${name} plugin encountered an error ${errorMessage}`,
|
||||
level: "warn",
|
||||
})
|
||||
}
|
||||
|
||||
private _fetchBranch = async () => {
|
||||
if (this._menuInstance.isOpen() && this._menuInstance.selectedItem) {
|
||||
try {
|
||||
await this._vcsProvider.fetchBranchFromRemote({
|
||||
currentDir: this._workspace.activeWorkspace,
|
||||
branch: this._menuInstance.selectedItem.label,
|
||||
})
|
||||
} catch (e) {
|
||||
this._notifyOfError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shelter the instance from the global scope -> globals are evil.
|
||||
function init() {
|
||||
let Provider: VersionControlManager
|
||||
|
||||
const Activate = (
|
||||
workspace: IWorkspace,
|
||||
editorManager: Oni.EditorManager,
|
||||
statusBar: Oni.StatusBar,
|
||||
commands: Oni.Commands.Api,
|
||||
menu: MenuManager,
|
||||
sidebar: SidebarManager,
|
||||
notifications: Notifications,
|
||||
configuration: Oni.Configuration,
|
||||
): void => {
|
||||
Provider = new VersionControlManager(
|
||||
workspace,
|
||||
editorManager,
|
||||
statusBar,
|
||||
menu,
|
||||
commands,
|
||||
sidebar,
|
||||
notifications,
|
||||
configuration,
|
||||
)
|
||||
}
|
||||
|
||||
const GetInstance = () => {
|
||||
return Provider
|
||||
}
|
||||
|
||||
return {
|
||||
activate: Activate,
|
||||
getInstance: GetInstance,
|
||||
}
|
||||
}
|
||||
export const { activate, getInstance } = init()
|
|
@ -0,0 +1,111 @@
|
|||
import * as capitalize from "lodash/capitalize"
|
||||
import * as Oni from "oni-api"
|
||||
import * as Log from "oni-core-logging"
|
||||
import * as React from "react"
|
||||
import { Provider, Store } from "react-redux"
|
||||
|
||||
import { VersionControlProvider, VersionControlView } from "./"
|
||||
import { IWorkspace } from "./../Workspace"
|
||||
import { ISendVCSNotification } from "./VersionControlManager"
|
||||
import { VersionControlState } from "./VersionControlStore"
|
||||
|
||||
export default class VersionControlPane {
|
||||
public get id(): string {
|
||||
return "oni.sidebar.vcs"
|
||||
}
|
||||
|
||||
public get title(): string {
|
||||
return capitalize(this._vcsProvider.name)
|
||||
}
|
||||
|
||||
constructor(
|
||||
private _editorManager: Oni.EditorManager,
|
||||
private _workspace: IWorkspace,
|
||||
private _vcsProvider: VersionControlProvider,
|
||||
private _sendNotification: ISendVCSNotification,
|
||||
private _store: Store<VersionControlState>,
|
||||
) {
|
||||
this._editorManager.activeEditor.onBufferSaved.subscribe(async () => {
|
||||
await this.getStatus()
|
||||
})
|
||||
|
||||
this._vcsProvider.onBranchChanged.subscribe(async () => {
|
||||
await this.getStatus()
|
||||
})
|
||||
|
||||
this._vcsProvider.onStagedFilesChanged.subscribe(async () => {
|
||||
await this.getStatus()
|
||||
})
|
||||
|
||||
this._vcsProvider.onPluginActivated.subscribe(async () => {
|
||||
this._store.dispatch({ type: "ACTIVATE" })
|
||||
await this.getStatus()
|
||||
})
|
||||
|
||||
this._vcsProvider.onPluginDeactivated.subscribe(() => {
|
||||
this._store.dispatch({ type: "DEACTIVATE" })
|
||||
})
|
||||
}
|
||||
|
||||
public enter(): void {
|
||||
this._store.dispatch({ type: "ENTER" })
|
||||
this._workspace.onDirectoryChanged.subscribe(async () => {
|
||||
await this.getStatus()
|
||||
})
|
||||
}
|
||||
|
||||
public leave(): void {
|
||||
this._store.dispatch({ type: "LEAVE" })
|
||||
}
|
||||
|
||||
public getStatus = async () => {
|
||||
const status = await this._vcsProvider.getStatus()
|
||||
if (status) {
|
||||
this._store.dispatch({ type: "STATUS", payload: { status } })
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
public stageFile = async (file: string) => {
|
||||
const { activeWorkspace } = this._workspace
|
||||
try {
|
||||
await this._vcsProvider.stageFile(file, activeWorkspace)
|
||||
} catch (e) {
|
||||
this._sendNotification({
|
||||
detail: e.message,
|
||||
level: "warn",
|
||||
title: "Error Staging File",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public setError = async (e: Error) => {
|
||||
Log.warn(`version control pane failed to render due to ${e.message}`)
|
||||
this._store.dispatch({ type: "ERROR" })
|
||||
}
|
||||
|
||||
public handleSelection = async (file: string): Promise<void> => {
|
||||
const { status } = this._store.getState()
|
||||
switch (true) {
|
||||
case status.untracked.includes(file):
|
||||
case status.modified.includes(file):
|
||||
await this.stageFile(file)
|
||||
break
|
||||
case status.staged.includes(file):
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<Provider store={this._store}>
|
||||
<VersionControlView
|
||||
setError={this.setError}
|
||||
getStatus={this.getStatus}
|
||||
handleSelection={this.handleSelection}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
import { IEvent } from "oni-types"
|
||||
import { BranchSummary, FetchResult } from "simple-git/promise"
|
||||
|
||||
export type BranchChangedEvent = string
|
||||
export type StagedFilesChangedEvent = string
|
||||
export interface FileStatusChangedEvent {
|
||||
path: string
|
||||
status: "staged"
|
||||
}
|
||||
|
||||
export interface StatusResult {
|
||||
ahead: number
|
||||
behind: number
|
||||
currentBranch: string
|
||||
modified: string[]
|
||||
staged: string[]
|
||||
conflicted: string[]
|
||||
created: string[]
|
||||
deleted: string[]
|
||||
untracked: string[]
|
||||
remoteTrackingBranch: string
|
||||
}
|
||||
|
||||
export interface VersionControlProvider {
|
||||
// Events
|
||||
onFileStatusChanged: IEvent<FileStatusChangedEvent>
|
||||
onStagedFilesChanged: IEvent<StagedFilesChangedEvent>
|
||||
onBranchChanged: IEvent<BranchChangedEvent>
|
||||
onPluginActivated: IEvent<void>
|
||||
onPluginDeactivated: IEvent<void>
|
||||
|
||||
name: SupportedProviders
|
||||
isActivated: boolean
|
||||
deactivate(): void
|
||||
activate(): void
|
||||
canHandleWorkspace(dir?: string): Promise<boolean>
|
||||
getStatus(): Promise<StatusResult | void>
|
||||
getRoot(): Promise<string | void>
|
||||
getDiff(): Promise<Diff | void>
|
||||
getBranch(): Promise<string | void>
|
||||
getLocalBranches(): Promise<BranchSummary | void>
|
||||
changeBranch(branch: string): Promise<void>
|
||||
stageFile(file: string, projectRoot?: string): Promise<void>
|
||||
fetchBranchFromRemote(args: {
|
||||
branch: string
|
||||
origin?: string
|
||||
currentDir: string
|
||||
}): Promise<FetchResult>
|
||||
}
|
||||
|
||||
export interface DiffResultTextFile {
|
||||
file: string
|
||||
changes: number
|
||||
insertions: number
|
||||
deletions: number
|
||||
binary: boolean
|
||||
}
|
||||
|
||||
export interface DiffResultBinaryFile {
|
||||
file: string
|
||||
before: number
|
||||
after: number
|
||||
binary: boolean
|
||||
}
|
||||
|
||||
export interface Diff {
|
||||
files: Array<DiffResultTextFile | DiffResultBinaryFile>
|
||||
insertions: number
|
||||
deletions: number
|
||||
}
|
||||
|
||||
export type Summary = StatusResult
|
||||
export type SupportedProviders = "git" | "svn"
|
||||
export default VersionControlProvider
|
|
@ -0,0 +1,80 @@
|
|||
import { createStore as createReduxStore } from "./../../Redux"
|
||||
import { StatusResult } from "./VersionControlProvider"
|
||||
|
||||
export interface VersionControlState {
|
||||
status: StatusResult
|
||||
hasFocus: boolean
|
||||
hasError: boolean
|
||||
activated: boolean
|
||||
}
|
||||
|
||||
interface IGenericAction<T, P = undefined> {
|
||||
type: T
|
||||
payload?: P
|
||||
}
|
||||
|
||||
export const DefaultState: VersionControlState = {
|
||||
status: {
|
||||
currentBranch: null,
|
||||
staged: [],
|
||||
conflicted: [],
|
||||
created: [],
|
||||
modified: [],
|
||||
remoteTrackingBranch: null,
|
||||
deleted: [],
|
||||
untracked: [],
|
||||
ahead: null,
|
||||
behind: null,
|
||||
},
|
||||
hasFocus: null,
|
||||
activated: null,
|
||||
hasError: false,
|
||||
}
|
||||
|
||||
type IActivateAction = IGenericAction<"ACTIVATE">
|
||||
type IDeactivateAction = IGenericAction<"DEACTIVATE">
|
||||
type IEnterAction = IGenericAction<"ENTER">
|
||||
type ILeaveAction = IGenericAction<"LEAVE">
|
||||
type IErrorAction = IGenericAction<"ERROR">
|
||||
type IStatusAction = IGenericAction<"STATUS", { status: StatusResult }>
|
||||
type IAction =
|
||||
| IStatusAction
|
||||
| IEnterAction
|
||||
| ILeaveAction
|
||||
| IErrorAction
|
||||
| IDeactivateAction
|
||||
| IActivateAction
|
||||
|
||||
export function reducer(state: VersionControlState, action: IAction) {
|
||||
switch (action.type) {
|
||||
case "ENTER":
|
||||
return { ...state, hasFocus: true }
|
||||
case "LEAVE":
|
||||
return { ...state, hasFocus: false }
|
||||
case "STATUS":
|
||||
return {
|
||||
...state,
|
||||
status: action.payload.status,
|
||||
}
|
||||
case "DEACTIVATE":
|
||||
return {
|
||||
...state,
|
||||
activated: false,
|
||||
status: DefaultState.status,
|
||||
}
|
||||
case "ACTIVATE":
|
||||
return {
|
||||
...state,
|
||||
activated: true,
|
||||
}
|
||||
case "ERROR":
|
||||
return {
|
||||
...state,
|
||||
hasError: true,
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
export default createReduxStore("Version Control", reducer, DefaultState)
|
|
@ -0,0 +1,226 @@
|
|||
import * as path from "path"
|
||||
import * as React from "react"
|
||||
import { connect } from "react-redux"
|
||||
|
||||
import { Icon } from "../../UI/Icon"
|
||||
import Caret from "./../../UI/components/Caret"
|
||||
import { css, styled, withProps } from "./../../UI/components/common"
|
||||
import { Sneakable } from "./../../UI/components/Sneakable"
|
||||
import { VimNavigator } from "./../../UI/components/VimNavigator"
|
||||
import { StatusResult } from "./VersionControlProvider"
|
||||
import { VersionControlState } from "./VersionControlStore"
|
||||
|
||||
const Row = styled.div`
|
||||
display: flex;
|
||||
span > {
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
`
|
||||
|
||||
interface SelectionProps {
|
||||
isSelected?: boolean
|
||||
}
|
||||
|
||||
const selected = css`
|
||||
border: ${(p: any) =>
|
||||
p.isSelected && `1px solid ${p.theme["highlight.mode.normal.background"]}`};
|
||||
`
|
||||
|
||||
const Column = withProps<SelectionProps>(styled.div)`
|
||||
${selected};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.3em;
|
||||
`
|
||||
|
||||
const Name = styled.span`
|
||||
margin-left: 0.5em;
|
||||
word-wrap: break-word;
|
||||
`
|
||||
|
||||
const Title = styled.h4`
|
||||
margin: 0;
|
||||
`
|
||||
|
||||
export const SectionTitle = withProps<SelectionProps>(styled.div)`
|
||||
${selected};
|
||||
margin: 0.2em 0;
|
||||
padding: 0.2em;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
`
|
||||
|
||||
interface IModifiedFilesProps {
|
||||
files?: string[]
|
||||
titleId: string
|
||||
selectedId: string
|
||||
icon: string
|
||||
onClick: (id: string) => void
|
||||
toggleVisibility: () => void
|
||||
visibility: boolean
|
||||
}
|
||||
|
||||
const truncate = (str: string) =>
|
||||
str
|
||||
.split(path.sep)
|
||||
.slice(-2)
|
||||
.join(path.sep)
|
||||
|
||||
export const GitStatus = ({
|
||||
files,
|
||||
selectedId,
|
||||
icon,
|
||||
onClick,
|
||||
toggleVisibility,
|
||||
titleId,
|
||||
visibility,
|
||||
}: IModifiedFilesProps) =>
|
||||
files && (
|
||||
<div>
|
||||
<SectionTitle
|
||||
isSelected={selectedId === titleId}
|
||||
data-test={`${titleId}-${files.length}`}
|
||||
onClick={toggleVisibility}
|
||||
>
|
||||
<Caret active={visibility && !!files.length} />
|
||||
<Title>{titleId.toUpperCase()}</Title>
|
||||
<strong>{files.length}</strong>
|
||||
</SectionTitle>
|
||||
{visibility &&
|
||||
files.map(filePath => (
|
||||
<Sneakable callback={() => onClick(filePath)} key={filePath}>
|
||||
<Column
|
||||
onClick={() => onClick(filePath)}
|
||||
isSelected={selectedId === filePath}
|
||||
>
|
||||
<Row>
|
||||
<Icon name={icon} />
|
||||
<Name>{truncate(filePath)}</Name>
|
||||
</Row>
|
||||
</Column>
|
||||
</Sneakable>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
const StatusContainer = styled.div`
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
`
|
||||
|
||||
interface IProps {
|
||||
status: StatusResult
|
||||
hasFocus: boolean
|
||||
hasError: boolean
|
||||
activated: boolean
|
||||
setError?: (e: Error) => void
|
||||
getStatus?: () => Promise<StatusResult | void>
|
||||
handleSelection?: (selection: string) => void
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
interface State {
|
||||
modified: boolean
|
||||
staged: boolean
|
||||
untracked: boolean
|
||||
}
|
||||
|
||||
export class VersionControlView extends React.Component<IProps, State> {
|
||||
public state: State = {
|
||||
modified: true,
|
||||
staged: true,
|
||||
untracked: true,
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
await this.props.getStatus()
|
||||
}
|
||||
|
||||
public async componentDidCatch(e: Error) {
|
||||
this.props.setError(e)
|
||||
}
|
||||
|
||||
public toggleVisibility = (section: keyof State) => {
|
||||
this.setState(prevState => ({ ...prevState, [section]: !prevState[section] }))
|
||||
}
|
||||
|
||||
public toggleOrAction = (id: string) => {
|
||||
if (id === "modified" || id === "staged" || id === "untracked") {
|
||||
this.toggleVisibility(id)
|
||||
}
|
||||
this.props.handleSelection(id)
|
||||
}
|
||||
|
||||
public insertIf(condition: boolean, element: string[]) {
|
||||
return condition ? element : []
|
||||
}
|
||||
|
||||
public render() {
|
||||
const error = this.props.hasError && "Something Went Wrong!"
|
||||
const inactive = !this.props.activated && "Version Control Not Available"
|
||||
const warning = error || inactive
|
||||
const { modified, staged, untracked } = this.props.status
|
||||
const ids = [
|
||||
"modified",
|
||||
...this.insertIf(this.state.modified, modified),
|
||||
"staged",
|
||||
...this.insertIf(this.state.staged, staged),
|
||||
"untracked",
|
||||
...this.insertIf(this.state.untracked, untracked),
|
||||
]
|
||||
|
||||
return warning ? (
|
||||
<SectionTitle>
|
||||
<Title>{warning}</Title>
|
||||
</SectionTitle>
|
||||
) : (
|
||||
<VimNavigator
|
||||
ids={ids}
|
||||
active={this.props.hasFocus}
|
||||
onSelected={this.toggleOrAction}
|
||||
render={selectedId => (
|
||||
<StatusContainer>
|
||||
<GitStatus
|
||||
icon="minus-circle"
|
||||
files={modified}
|
||||
titleId="modified"
|
||||
selectedId={selectedId}
|
||||
visibility={this.state.modified}
|
||||
onClick={this.props.handleSelection}
|
||||
toggleVisibility={() => this.toggleVisibility("modified")}
|
||||
/>
|
||||
<GitStatus
|
||||
icon="plus-circle"
|
||||
titleId="staged"
|
||||
files={staged}
|
||||
selectedId={selectedId}
|
||||
visibility={this.state.staged}
|
||||
onClick={this.props.handleSelection}
|
||||
toggleVisibility={() => this.toggleVisibility("staged")}
|
||||
/>
|
||||
<GitStatus
|
||||
files={untracked}
|
||||
icon="question-circle"
|
||||
titleId="untracked"
|
||||
selectedId={selectedId}
|
||||
visibility={this.state.untracked}
|
||||
onClick={this.props.handleSelection}
|
||||
toggleVisibility={() => this.toggleVisibility("untracked")}
|
||||
/>
|
||||
</StatusContainer>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect<VersionControlState>(
|
||||
(state: VersionControlState): IProps => ({
|
||||
status: state.status,
|
||||
hasFocus: state.hasFocus,
|
||||
hasError: state.hasError,
|
||||
activated: state.activated,
|
||||
}),
|
||||
null,
|
||||
)(VersionControlView)
|
|
@ -0,0 +1,10 @@
|
|||
export {
|
||||
Diff,
|
||||
Summary,
|
||||
SupportedProviders,
|
||||
default as VersionControlProvider,
|
||||
} from "./VersionControlProvider"
|
||||
export { activate, getInstance, VersionControlManager } from "./VersionControlManager"
|
||||
export { default as VersionControlPane } from "./VersionControlPane"
|
||||
export { default as store } from "./VersionControlStore"
|
||||
export { default as VersionControlView } from "./VersionControlView"
|
|
@ -0,0 +1,12 @@
|
|||
import * as React from "react"
|
||||
|
||||
const Caret = ({ active }: { active: boolean }) => {
|
||||
const caretStyle = {
|
||||
transform: active ? "rotateZ(45deg)" : "rotateZ(0deg)",
|
||||
transition: "transform 0.1s ease-in",
|
||||
}
|
||||
|
||||
return <i style={caretStyle} className="fa fa-caret-right" />
|
||||
}
|
||||
|
||||
export default Caret
|
|
@ -8,6 +8,7 @@ import * as React from "react"
|
|||
|
||||
import { styled, withProps } from "./common"
|
||||
|
||||
import Caret from "./../../UI/components/Caret"
|
||||
import { Sneakable } from "./../../UI/components/Sneakable"
|
||||
|
||||
export interface ISidebarItemViewProps {
|
||||
|
@ -129,11 +130,6 @@ const SidebarContainer = withProps<IContainerProps>(styled.div)`
|
|||
|
||||
export class SidebarContainerView extends React.PureComponent<ISidebarContainerViewProps, {}> {
|
||||
public render(): JSX.Element {
|
||||
const caretStyle = {
|
||||
transform: this.props.isExpanded ? "rotateZ(45deg)" : "rotateZ(0deg)",
|
||||
transition: "transform 0.1s ease-in",
|
||||
}
|
||||
const icon = <i style={caretStyle} className="fa fa-caret-right" />
|
||||
const indentationlevel = this.props.indentationLevel || 0
|
||||
|
||||
return (
|
||||
|
@ -148,7 +144,7 @@ export class SidebarContainerView extends React.PureComponent<ISidebarContainerV
|
|||
updated={this.props.updated}
|
||||
didDrop={this.props.didDrop}
|
||||
indentationLevel={indentationlevel}
|
||||
icon={icon}
|
||||
icon={<Caret active={this.props.isExpanded} />}
|
||||
text={this.props.text}
|
||||
isFocused={this.props.isFocused}
|
||||
isContainer={this.props.isContainer}
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
import * as React from "react"
|
||||
|
||||
import { Diff } from "./../../Services/VersionControl"
|
||||
import styled, { IThemeColors, withProps } from "./../../UI/components/common"
|
||||
import { Icon } from "./../../UI/Icon"
|
||||
|
||||
type ChangeTypes = "change" | "addition" | "deletion"
|
||||
|
||||
interface ICreateIconArgs {
|
||||
type: ChangeTypes
|
||||
num: number
|
||||
}
|
||||
|
||||
const BranchContainer = styled.div`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const BranchText = styled.span`
|
||||
min-width: 10px;
|
||||
text-align: center;
|
||||
padding: 2px 4px 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
export const BranchNameContainer = styled.span`
|
||||
width: 100%;
|
||||
margin-left: 4px;
|
||||
`
|
||||
|
||||
const selectColorByType = (type: ChangeTypes, theme: IThemeColors) => {
|
||||
switch (type) {
|
||||
case "addition":
|
||||
case "deletion":
|
||||
case "change":
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
const ChangeSpanContainer = withProps<{ type: ChangeTypes }>(styled.span)`
|
||||
font-size: 0.7rem;
|
||||
padding: 0 0.15rem;
|
||||
color: ${({ type, theme }) => selectColorByType(type, theme)};
|
||||
`
|
||||
|
||||
const ChangeSpan = styled.span`
|
||||
padding-left: 0.25rem;
|
||||
`
|
||||
|
||||
interface BranchProps {
|
||||
branch: string
|
||||
children?: React.ReactNode
|
||||
diff: Diff
|
||||
}
|
||||
|
||||
export const Branch: React.SFC<BranchProps> = ({ diff, branch, children }) =>
|
||||
branch && (
|
||||
<BranchContainer>
|
||||
<BranchText>
|
||||
<Icon name="code-fork" />
|
||||
<BranchNameContainer>
|
||||
{`${branch} `}
|
||||
{diff && (
|
||||
<DeletionsAndInsertions
|
||||
deletions={diff.deletions}
|
||||
insertions={diff.insertions}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</BranchNameContainer>
|
||||
</BranchText>
|
||||
</BranchContainer>
|
||||
)
|
||||
|
||||
const getClassNameForType = (type: ChangeTypes) => {
|
||||
switch (type) {
|
||||
case "addition":
|
||||
return "plus-circle"
|
||||
case "deletion":
|
||||
return "minus-circle"
|
||||
case "change":
|
||||
default:
|
||||
return "question-circle"
|
||||
}
|
||||
}
|
||||
|
||||
interface ChangesProps {
|
||||
deletions: number
|
||||
insertions: number
|
||||
}
|
||||
|
||||
export const DeletionsAndInsertions: React.SFC<ChangesProps> = ({ deletions, insertions }) => (
|
||||
<span>
|
||||
<VCSIcon type="addition" num={insertions} />
|
||||
{!!(deletions && insertions) && <span key={2}>, </span>}
|
||||
<VCSIcon type="deletion" num={deletions} />
|
||||
</span>
|
||||
)
|
||||
|
||||
export const VCSIcon: React.SFC<ICreateIconArgs> = ({ type, num }) =>
|
||||
!!num && (
|
||||
<span>
|
||||
<ChangeSpanContainer type={type}>
|
||||
<Icon name={getClassNameForType(type)} />
|
||||
</ChangeSpanContainer>
|
||||
<ChangeSpan data-test={`${type}-${num}`}>{num}</ChangeSpan>
|
||||
</span>
|
||||
)
|
|
@ -6,7 +6,7 @@ import * as assert from "assert"
|
|||
import * as path from "path"
|
||||
|
||||
import { Store } from "redux"
|
||||
import { MockStoreCreator } from "redux-mock-store"
|
||||
import configureMockStore, { MockStoreCreator } from "redux-mock-store"
|
||||
import { ActionsObservable, combineEpics, createEpicMiddleware } from "redux-observable"
|
||||
|
||||
import * as ExplorerFileSystem from "./../../../src/Services/Explorer/ExplorerFileSystem"
|
||||
|
@ -18,8 +18,6 @@ import * as clone from "lodash/clone"
|
|||
import * as head from "lodash/head"
|
||||
import * as TestHelpers from "./../../TestHelpers"
|
||||
|
||||
const configureMockStore = require("redux-mock-store") // tslint:disable-line
|
||||
|
||||
export class MockedFileSystem implements ExplorerFileSystem.IFileSystem {
|
||||
public promises: Array<Promise<any>>
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ module.exports = {
|
|||
"keyboard-layout": "require('keyboard-layout')",
|
||||
gifshot: "require('gifshot')",
|
||||
"msgpack-lite": "require('msgpack-lite')",
|
||||
"simple-git/promise": "require('simple-git/promise')",
|
||||
"styled-components": "require('styled-components')",
|
||||
fsevents: "require('fsevents')",
|
||||
},
|
||||
|
|
|
@ -43,6 +43,7 @@ if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
|||
|
||||
ls -a lib_test/browser/src_ccov
|
||||
|
||||
npm run ccov:upload:jest
|
||||
npm run ccov:test:browser
|
||||
npm run ccov:remap:browser:lcov
|
||||
npm run ccov:clean
|
||||
|
|
|
@ -2,8 +2,7 @@ module.exports = {
|
|||
bail: true,
|
||||
verbose: true,
|
||||
collectCoverage: true,
|
||||
coverageDirectory: "<rootDir>/coverage/",
|
||||
collectCoverageFrom: ["**/components/*.{tsx}", "!**/node_modules/**", "!**/dist/**"],
|
||||
coverageDirectory: "<rootDir>/coverage/jest/",
|
||||
setupFiles: ["<rootDir>/ui-tests/jestsetup.ts"],
|
||||
moduleNameMapper: {
|
||||
electron: "<rootDir>/ui-tests/mocks/electronMock.ts",
|
||||
|
|
411
package.json
411
package.json
|
@ -5,14 +5,7 @@
|
|||
"homepage": "https://www.onivim.io",
|
||||
"version": "0.3.7",
|
||||
"description": "Code editor with a modern twist on modal editing - powered by neovim.",
|
||||
"keywords": [
|
||||
"vim",
|
||||
"neovim",
|
||||
"text",
|
||||
"editor",
|
||||
"ide",
|
||||
"vim"
|
||||
],
|
||||
"keywords": ["vim", "neovim", "text", "editor", "ide", "vim"],
|
||||
"main": "./lib/main/src/main.js",
|
||||
"bin": {
|
||||
"oni": "./lib/cli/src/cli.js",
|
||||
|
@ -50,39 +43,23 @@
|
|||
"mac": {
|
||||
"artifactName": "${productName}-${version}-osx.${ext}",
|
||||
"category": "public.app-category.developer-tools",
|
||||
"target": [
|
||||
"dmg"
|
||||
],
|
||||
"files": [
|
||||
"bin/osx/**/*"
|
||||
]
|
||||
"target": ["dmg"],
|
||||
"files": ["bin/osx/**/*"]
|
||||
},
|
||||
"linux": {
|
||||
"artifactName": "${productName}-${version}-${arch}-linux.${ext}",
|
||||
"maintainer": "bryphe@outlook.com",
|
||||
"target": [
|
||||
"tar.gz",
|
||||
"deb",
|
||||
"rpm"
|
||||
]
|
||||
"target": ["tar.gz", "deb", "rpm"]
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
"zip",
|
||||
"dir"
|
||||
],
|
||||
"files": [
|
||||
"bin/x86/**/*"
|
||||
]
|
||||
"target": ["zip", "dir"],
|
||||
"files": ["bin/x86/**/*"]
|
||||
},
|
||||
"fileAssociations": [
|
||||
{
|
||||
"name": "ADA source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"adb",
|
||||
"ads"
|
||||
]
|
||||
"ext": ["adb", "ads"]
|
||||
},
|
||||
{
|
||||
"name": "Compiled AppleScript",
|
||||
|
@ -102,20 +79,12 @@
|
|||
{
|
||||
"name": "ASP document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"asp",
|
||||
"asa"
|
||||
]
|
||||
"ext": ["asp", "asa"]
|
||||
},
|
||||
{
|
||||
"name": "ASP.NET document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"aspx",
|
||||
"ascx",
|
||||
"asmx",
|
||||
"ashx"
|
||||
]
|
||||
"ext": ["aspx", "ascx", "asmx", "ashx"]
|
||||
},
|
||||
{
|
||||
"name": "BibTeX bibliography",
|
||||
|
@ -130,13 +99,7 @@
|
|||
{
|
||||
"name": "C++ source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"cc",
|
||||
"cp",
|
||||
"cpp",
|
||||
"cxx",
|
||||
"c++"
|
||||
]
|
||||
"ext": ["cc", "cp", "cpp", "cxx", "c++"]
|
||||
},
|
||||
{
|
||||
"name": "C# source",
|
||||
|
@ -161,10 +124,7 @@
|
|||
{
|
||||
"name": "Clojure source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"clj",
|
||||
"cljs"
|
||||
]
|
||||
"ext": ["clj", "cljs"]
|
||||
},
|
||||
{
|
||||
"name": "Comma separated values",
|
||||
|
@ -179,20 +139,12 @@
|
|||
{
|
||||
"name": "CGI script",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"cgi",
|
||||
"fcgi"
|
||||
]
|
||||
"ext": ["cgi", "fcgi"]
|
||||
},
|
||||
{
|
||||
"name": "Configuration file",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"cfg",
|
||||
"conf",
|
||||
"config",
|
||||
"htaccess"
|
||||
]
|
||||
"ext": ["cfg", "conf", "config", "htaccess"]
|
||||
},
|
||||
{
|
||||
"name": "Cascading style sheet",
|
||||
|
@ -217,10 +169,7 @@
|
|||
{
|
||||
"name": "Erlang source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"erl",
|
||||
"hrl"
|
||||
]
|
||||
"ext": ["erl", "hrl"]
|
||||
},
|
||||
{
|
||||
"name": "F-Script source",
|
||||
|
@ -230,32 +179,17 @@
|
|||
{
|
||||
"name": "Fortran source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"f",
|
||||
"for",
|
||||
"fpp",
|
||||
"f77",
|
||||
"f90",
|
||||
"f95"
|
||||
]
|
||||
"ext": ["f", "for", "fpp", "f77", "f90", "f95"]
|
||||
},
|
||||
{
|
||||
"name": "Header",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"h",
|
||||
"pch"
|
||||
]
|
||||
"ext": ["h", "pch"]
|
||||
},
|
||||
{
|
||||
"name": "C++ header",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"hh",
|
||||
"hpp",
|
||||
"hxx",
|
||||
"h++"
|
||||
]
|
||||
"ext": ["hh", "hpp", "hxx", "h++"]
|
||||
},
|
||||
{
|
||||
"name": "Go source",
|
||||
|
@ -265,28 +199,17 @@
|
|||
{
|
||||
"name": "GTD document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"gtd",
|
||||
"gtdlog"
|
||||
]
|
||||
"ext": ["gtd", "gtdlog"]
|
||||
},
|
||||
{
|
||||
"name": "Haskell source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"hs",
|
||||
"lhs"
|
||||
]
|
||||
"ext": ["hs", "lhs"]
|
||||
},
|
||||
{
|
||||
"name": "HTML document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"htm",
|
||||
"html",
|
||||
"phtml",
|
||||
"shtml"
|
||||
]
|
||||
"ext": ["htm", "html", "phtml", "shtml"]
|
||||
},
|
||||
{
|
||||
"name": "Include file",
|
||||
|
@ -326,10 +249,7 @@
|
|||
{
|
||||
"name": "JavaScript source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"js",
|
||||
"htc"
|
||||
]
|
||||
"ext": ["js", "htc"]
|
||||
},
|
||||
{
|
||||
"name": "Java Server Page",
|
||||
|
@ -354,14 +274,7 @@
|
|||
{
|
||||
"name": "Lisp source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"lisp",
|
||||
"cl",
|
||||
"l",
|
||||
"lsp",
|
||||
"mud",
|
||||
"el"
|
||||
]
|
||||
"ext": ["lisp", "cl", "l", "lsp", "mud", "el"]
|
||||
},
|
||||
{
|
||||
"name": "Log file",
|
||||
|
@ -381,12 +294,7 @@
|
|||
{
|
||||
"name": "Markdown document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"markdown",
|
||||
"mdown",
|
||||
"markdn",
|
||||
"md"
|
||||
]
|
||||
"ext": ["markdown", "mdown", "markdn", "md"]
|
||||
},
|
||||
{
|
||||
"name": "Makefile source",
|
||||
|
@ -396,29 +304,17 @@
|
|||
{
|
||||
"name": "Mediawiki document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"wiki",
|
||||
"wikipedia",
|
||||
"mediawiki"
|
||||
]
|
||||
"ext": ["wiki", "wikipedia", "mediawiki"]
|
||||
},
|
||||
{
|
||||
"name": "MIPS assembler source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"s",
|
||||
"mips",
|
||||
"spim",
|
||||
"asm"
|
||||
]
|
||||
"ext": ["s", "mips", "spim", "asm"]
|
||||
},
|
||||
{
|
||||
"name": "Modula-3 source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"m3",
|
||||
"cm3"
|
||||
]
|
||||
"ext": ["m3", "cm3"]
|
||||
},
|
||||
{
|
||||
"name": "MoinMoin document",
|
||||
|
@ -438,28 +334,17 @@
|
|||
{
|
||||
"name": "OCaml source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"ml",
|
||||
"mli",
|
||||
"mll",
|
||||
"mly"
|
||||
]
|
||||
"ext": ["ml", "mli", "mll", "mly"]
|
||||
},
|
||||
{
|
||||
"name": "Mustache document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"mustache",
|
||||
"hbs"
|
||||
]
|
||||
"ext": ["mustache", "hbs"]
|
||||
},
|
||||
{
|
||||
"name": "Pascal source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"pas",
|
||||
"p"
|
||||
]
|
||||
"ext": ["pas", "p"]
|
||||
},
|
||||
{
|
||||
"name": "Patch file",
|
||||
|
@ -469,11 +354,7 @@
|
|||
{
|
||||
"name": "Perl source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"pl",
|
||||
"pod",
|
||||
"perl"
|
||||
]
|
||||
"ext": ["pl", "pod", "perl"]
|
||||
},
|
||||
{
|
||||
"name": "Perl module",
|
||||
|
@ -483,80 +364,47 @@
|
|||
{
|
||||
"name": "PHP source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"php",
|
||||
"php3",
|
||||
"php4",
|
||||
"php5"
|
||||
]
|
||||
"ext": ["php", "php3", "php4", "php5"]
|
||||
},
|
||||
{
|
||||
"name": "PostScript source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"ps",
|
||||
"eps"
|
||||
]
|
||||
"ext": ["ps", "eps"]
|
||||
},
|
||||
{
|
||||
"name": "Property list",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"dict",
|
||||
"plist",
|
||||
"scriptSuite",
|
||||
"scriptTerminology"
|
||||
]
|
||||
"ext": ["dict", "plist", "scriptSuite", "scriptTerminology"]
|
||||
},
|
||||
{
|
||||
"name": "Python source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"py",
|
||||
"rpy",
|
||||
"cpy",
|
||||
"python"
|
||||
]
|
||||
"ext": ["py", "rpy", "cpy", "python"]
|
||||
},
|
||||
{
|
||||
"name": "R source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"r",
|
||||
"s"
|
||||
]
|
||||
"ext": ["r", "s"]
|
||||
},
|
||||
{
|
||||
"name": "Ragel source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"rl",
|
||||
"ragel"
|
||||
]
|
||||
"ext": ["rl", "ragel"]
|
||||
},
|
||||
{
|
||||
"name": "Remind document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"rem",
|
||||
"remind"
|
||||
]
|
||||
"ext": ["rem", "remind"]
|
||||
},
|
||||
{
|
||||
"name": "reStructuredText document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"rst",
|
||||
"rest"
|
||||
]
|
||||
"ext": ["rst", "rest"]
|
||||
},
|
||||
{
|
||||
"name": "HTML with embedded Ruby",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"rhtml",
|
||||
"erb"
|
||||
]
|
||||
"ext": ["rhtml", "erb"]
|
||||
},
|
||||
{
|
||||
"name": "SQL with embedded Ruby",
|
||||
|
@ -566,28 +414,17 @@
|
|||
{
|
||||
"name": "Ruby source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"rb",
|
||||
"rbx",
|
||||
"rjs",
|
||||
"rxml"
|
||||
]
|
||||
"ext": ["rb", "rbx", "rjs", "rxml"]
|
||||
},
|
||||
{
|
||||
"name": "Sass source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"sass",
|
||||
"scss"
|
||||
]
|
||||
"ext": ["sass", "scss"]
|
||||
},
|
||||
{
|
||||
"name": "Scheme source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"scm",
|
||||
"sch"
|
||||
]
|
||||
"ext": ["scm", "sch"]
|
||||
},
|
||||
{
|
||||
"name": "Setext document",
|
||||
|
@ -635,10 +472,7 @@
|
|||
{
|
||||
"name": "SWIG source",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"i",
|
||||
"swg"
|
||||
]
|
||||
"ext": ["i", "swg"]
|
||||
},
|
||||
{
|
||||
"name": "Tcl source",
|
||||
|
@ -648,20 +482,12 @@
|
|||
{
|
||||
"name": "TeX document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"tex",
|
||||
"sty",
|
||||
"cls"
|
||||
]
|
||||
"ext": ["tex", "sty", "cls"]
|
||||
},
|
||||
{
|
||||
"name": "Plain text document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"text",
|
||||
"txt",
|
||||
"utf8"
|
||||
]
|
||||
"ext": ["text", "txt", "utf8"]
|
||||
},
|
||||
{
|
||||
"name": "Textile document",
|
||||
|
@ -681,32 +507,17 @@
|
|||
{
|
||||
"name": "XML document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"xml",
|
||||
"xsd",
|
||||
"xib",
|
||||
"rss",
|
||||
"tld",
|
||||
"pt",
|
||||
"cpt",
|
||||
"dtml"
|
||||
]
|
||||
"ext": ["xml", "xsd", "xib", "rss", "tld", "pt", "cpt", "dtml"]
|
||||
},
|
||||
{
|
||||
"name": "XSL stylesheet",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"xsl",
|
||||
"xslt"
|
||||
]
|
||||
"ext": ["xsl", "xslt"]
|
||||
},
|
||||
{
|
||||
"name": "Electronic business card",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"vcf",
|
||||
"vcard"
|
||||
]
|
||||
"ext": ["vcf", "vcard"]
|
||||
},
|
||||
{
|
||||
"name": "Visual Basic source",
|
||||
|
@ -716,10 +527,7 @@
|
|||
{
|
||||
"name": "YAML document",
|
||||
"role": "Editor",
|
||||
"ext": [
|
||||
"yaml",
|
||||
"yml"
|
||||
]
|
||||
"ext": ["yaml", "yml"]
|
||||
},
|
||||
{
|
||||
"name": "Text document",
|
||||
|
@ -781,69 +589,104 @@
|
|||
"scripts": {
|
||||
"precommit": "pretty-quick --staged",
|
||||
"prepush": "yarn run build && yarn run lint",
|
||||
"build": "yarn run build:browser && yarn run build:webview_preload && yarn run build:main && yarn run build:plugins && yarn run build:cli",
|
||||
"build-debug": "yarn run build:browser-debug && yarn run build:main && yarn run build:plugins",
|
||||
"build":
|
||||
"yarn run build:browser && yarn run build:webview_preload && yarn run build:main && yarn run build:plugins",
|
||||
"build-debug":
|
||||
"yarn run build:browser-debug && yarn run build:main && yarn run build:plugins",
|
||||
"build:browser": "webpack --config browser/webpack.production.config.js",
|
||||
"build:browser-debug": "webpack --config browser/webpack.debug.config.js",
|
||||
"build:main": "cd main && tsc -p tsconfig.json",
|
||||
"build:plugins":
|
||||
"yarn run build:plugin:oni-plugin-typescript && yarn run build:plugin:oni-plugin-git && yarn run build:plugin:oni-plugin-markdown-preview",
|
||||
"build:cli": "cd cli && tsc -p tsconfig.json",
|
||||
"build:plugins": "yarn run build:plugin:oni-plugin-typescript && yarn run build:plugin:oni-plugin-markdown-preview",
|
||||
"build:plugin:oni-plugin-typescript": "cd vim/core/oni-plugin-typescript && yarn run build",
|
||||
"build:plugin:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && yarn run build",
|
||||
"build:plugin:oni-plugin-git": "cd vim/core/oni-plugin-git && yarn run build",
|
||||
"build:plugin:oni-plugin-markdown-preview":
|
||||
"cd extensions/oni-plugin-markdown-preview && yarn run build",
|
||||
"build:test": "yarn run build:test:unit && yarn run build:test:integration",
|
||||
"build:test:integration": "cd test && tsc -p tsconfig.json",
|
||||
"build:test:unit": "yarn run build:test:unit:browser && yarn run build:test:unit:main",
|
||||
"build:test:unit:browser": "rimraf lib_test/browser && cd browser && tsc -p tsconfig.test.json",
|
||||
"build:test:unit": "run-s build:test:unit:*",
|
||||
"build:test:unit:browser":
|
||||
"rimraf lib_test/browser && cd browser && tsc -p tsconfig.test.json",
|
||||
"build:test:unit:main": "rimraf lib_test/main && cd main && tsc -p tsconfig.test.json",
|
||||
"build:webview_preload": "cd webview_preload && tsc -p tsconfig.json",
|
||||
"check-cached-binaries": "node build/script/CheckBinariesForBuild.js",
|
||||
"copy-icons": "node build/CopyIcons.js",
|
||||
"debug:test:unit:browser": "cd browser && tsc -p tsconfig.test.json && electron-mocha --interactive --debug --renderer --require testHelpers.js --recursive ../lib_test/browser/test",
|
||||
"demo:screenshot": "yarn run build:test && cross-env DEMO_TEST=HeroScreenshot mocha -t 30000000 lib_test/test/Demo.js",
|
||||
"demo:video": "yarn run build:test && cross-env DEMO_TEST=HeroDemo mocha -t 30000000 lib_test/test/Demo.js",
|
||||
"dist:win:x86": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch ia32 --publish never",
|
||||
"dist:win:x64": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch x64 --publish never",
|
||||
"pack:win": "node build/BuildSetupTemplate.js && innosetup-compiler dist/setup.iss --verbose --O=dist",
|
||||
"debug:test:unit:browser":
|
||||
"cd browser && tsc -p tsconfig.test.json && electron-mocha --interactive --debug --renderer --require testHelpers.js --recursive ../lib_test/browser/test",
|
||||
"demo:screenshot":
|
||||
"yarn run build:test && cross-env DEMO_TEST=HeroScreenshot mocha -t 30000000 lib_test/test/Demo.js",
|
||||
"demo:video":
|
||||
"yarn run build:test && cross-env DEMO_TEST=HeroDemo mocha -t 30000000 lib_test/test/Demo.js",
|
||||
"dist:win:x86":
|
||||
"cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch ia32 --publish never",
|
||||
"dist:win:x64":
|
||||
"cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --arch x64 --publish never",
|
||||
"pack:win":
|
||||
"node build/BuildSetupTemplate.js && innosetup-compiler dist/setup.iss --verbose --O=dist",
|
||||
"test": "yarn run test:unit && yarn run test:integration",
|
||||
"test:setup": "yarn run build:test:integration && mocha -t 120000 --recursive lib_test/test/setup",
|
||||
"test:integration": "yarn run build:test:integration && mocha -t 120000 lib_test/test/CiTests.js --bail",
|
||||
"test:react": "jest --config ./jest.config.js ./ui-tests",
|
||||
"test:react:watch": "jest --config ./jest.config.js ./ui-tests --watch",
|
||||
"test:react:coverage": "jest --config ./jest.config.js ./ui-tests --coverage",
|
||||
"test:unit:browser": "yarn run build:test:unit:browser && cd browser && electron-mocha --renderer --require testHelpers.js --recursive ../lib_test/browser/test",
|
||||
"test:unit": "yarn run test:unit:browser && yarn run test:unit:main && yarn run test:react",
|
||||
"test:unit:main": "yarn run build:test:unit:main && cd main && electron-mocha --renderer --recursive ../lib_test/main/test",
|
||||
"test:setup":
|
||||
"yarn run build:test:integration && mocha -t 120000 --recursive lib_test/test/setup",
|
||||
"test:integration":
|
||||
"yarn run build:test:integration && mocha -t 120000 lib_test/test/CiTests.js --bail",
|
||||
"test:unit:react": "jest --config ./jest.config.js ./ui-tests",
|
||||
"test:unit:react:watch": "jest --config ./jest.config.js ./ui-tests --watch",
|
||||
"test:unit:react:coverage": "jest --config ./jest.config.js ./ui-tests --coverage",
|
||||
"test:unit:browser":
|
||||
"yarn run build:test:unit:browser && cd browser && electron-mocha --renderer --require testHelpers.js --recursive ../lib_test/browser/test",
|
||||
"test:unit":
|
||||
"yarn run test:unit:browser && yarn run test:unit:main && yarn run test:unit:react",
|
||||
"test:unit:main":
|
||||
"yarn run build:test:unit:main && cd main && electron-mocha --renderer --recursive ../lib_test/main/test",
|
||||
"upload:dist": "node build/script/UploadDistributionBuildsToAzure",
|
||||
"fix-lint": "yarn run fix-lint:browser && yarn run fix-lint:main && yarn run fix-lint:test",
|
||||
"fix-lint:browser": "tslint --fix --project browser/tsconfig.json --exclude **/node_modules/**/* --config tslint.json && tslint --fix --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --fix --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json",
|
||||
"fix-lint": "run-p fix-lint:*",
|
||||
"fix-lint:browser":
|
||||
"tslint --fix --project browser/tsconfig.json --exclude **/node_modules/**/* --config tslint.json && tslint --fix --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --fix --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json",
|
||||
"fix-lint:cli": "tslint --fix --project cli/tsconfig.json --config tslint.json",
|
||||
"fix-lint:main": "tslint --fix --project main/tsconfig.json --config tslint.json",
|
||||
"fix-lint:test": "tslint --fix --project test/tsconfig.json --config tslint.json",
|
||||
"lint": "yarn run lint:browser && yarn run lint:main && yarn run lint:test",
|
||||
"lint:browser": "tslint --project browser/tsconfig.json --config tslint.json && tslint --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json",
|
||||
"lint:browser":
|
||||
"tslint --project browser/tsconfig.json --config tslint.json && tslint --project vim/core/oni-plugin-typescript/tsconfig.json --config tslint.json && tslint --project extensions/oni-plugin-markdown-preview/tsconfig.json --config tslint.json",
|
||||
"lint:cli": "tslint --project cli/tsconfig.json --config tslint.json",
|
||||
"lint:main": "tslint --project main/tsconfig.json --config tslint.json",
|
||||
"lint:test": "tslint --project test/tsconfig.json --config tslint.json && tslint vim/core/oni-plugin-typescript/src/**/*.ts && tslint extensions/oni-plugin-markdown-preview/src/**/*.ts",
|
||||
"lint:test":
|
||||
"tslint --project test/tsconfig.json --config tslint.json && tslint vim/core/oni-plugin-typescript/src/**/*.ts && tslint extensions/oni-plugin-markdown-preview/src/**/*.ts",
|
||||
"pack": "cross-env ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES=1 build --publish never",
|
||||
"ccov:instrument": "nyc instrument --all true --sourceMap false lib_test/browser/src lib_test/browser/src_ccov",
|
||||
"ccov:test:browser": "cross-env ONI_CCOV=1 electron-mocha --renderer --require browser/testHelpers.js -R browser/testCoverageReporter --recursive lib_test/browser/test",
|
||||
"ccov:remap:browser:html": "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output html-report --type html",
|
||||
"ccov:remap:browser:lcov": "cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output lcov.info --type lcovonly",
|
||||
"ccov:instrument":
|
||||
"nyc instrument --all true --sourceMap false lib_test/browser/src lib_test/browser/src_ccov",
|
||||
"ccov:test:browser":
|
||||
"cross-env ONI_CCOV=1 electron-mocha --renderer --require browser/testHelpers.js -R browser/testCoverageReporter --recursive lib_test/browser/test",
|
||||
"ccov:remap:browser:html":
|
||||
"cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output html-report --type html",
|
||||
"ccov:remap:browser:lcov":
|
||||
"cd lib_test/browser/src && remap-istanbul --input ../../../coverage/coverage-final.json --output lcov.info --type lcovonly",
|
||||
"ccov:clean": "rimraf coverage",
|
||||
"ccov:upload:jest": "cd ./coverage/jest && codecov",
|
||||
"ccov:upload": "codecov",
|
||||
"launch": "electron lib/main/src/main.js",
|
||||
"start": "concurrently --kill-others \"yarn run start-hot\" \"yarn run watch:browser\" \"yarn run watch:plugins\"",
|
||||
"start-hot": "cross-env ONI_WEBPACK_LOAD=1 NODE_ENV=development electron lib/main/src/main.js",
|
||||
"start":
|
||||
"concurrently --kill-others \"yarn run start-hot\" \"yarn run watch:browser\" \"yarn run watch:plugins\"",
|
||||
"start-hot":
|
||||
"cross-env ONI_WEBPACK_LOAD=1 NODE_ENV=development electron lib/main/src/main.js",
|
||||
"start-not-dev": "cross-env electron main.js",
|
||||
"watch:browser": "webpack-dev-server --config browser/webpack.development.config.js --host localhost --port 8191",
|
||||
"watch:plugins": "yarn run watch:plugins:oni-plugin-typescript && yarn run watch:plugins:oni-plugin-markdown-preview",
|
||||
"watch:browser":
|
||||
"webpack-dev-server --config browser/webpack.development.config.js --host localhost --port 8191",
|
||||
"watch:plugins":
|
||||
"yarn watch:plugins:oni-plugin-typescript && watch:plugins:oni-plugin-markdown-preview && watch:plugins:oni-plugin-git",
|
||||
"watch:plugins:oni-plugin-typescript": "cd vim/core/oni-plugin-typescript && tsc --watch",
|
||||
"watch:plugins:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && tsc --watch",
|
||||
"install:plugins": "yarn run install:plugins:oni-plugin-markdown-preview && yarn run install:plugins:oni-plugin-prettier",
|
||||
"install:plugins:oni-plugin-markdown-preview": "cd extensions/oni-plugin-markdown-preview && yarn install --prod",
|
||||
"install:plugins:oni-plugin-prettier": "cd extensions/oni-plugin-prettier && yarn install --prod",
|
||||
"watch:plugins:oni-plugin-markdown-preview":
|
||||
"cd extensions/oni-plugin-markdown-preview && tsc --watch",
|
||||
"watch:plugins:oni-plugin-git": "cd vim/core/oni-plugin-git && tsc --watch",
|
||||
"install:plugins": "run-s install:plugins:*",
|
||||
"install:plugins:oni-plugin-markdown-preview":
|
||||
"cd extensions/oni-plugin-markdown-preview && yarn install --prod",
|
||||
"install:plugins:oni-plugin-prettier":
|
||||
"cd extensions/oni-plugin-prettier && yarn install --prod",
|
||||
"install:plugins:oni-plugin-git": "cd vim/core/oni-plugin-git && yarn install --prod",
|
||||
"postinstall": "yarn run install:plugins && electron-rebuild && opencollective postinstall",
|
||||
"profile:webpack": "webpack --config browser/webpack.production.config.js --profile --json > stats.json && webpack-bundle-analyzer browser/stats.json"
|
||||
"profile:webpack":
|
||||
"webpack --config browser/webpack.production.config.js --profile --json > stats.json && webpack-bundle-analyzer browser/stats.json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -874,6 +717,7 @@
|
|||
"redux-batched-subscribe": "^0.1.6",
|
||||
"shell-env": "^0.3.0",
|
||||
"shelljs": "0.7.7",
|
||||
"simple-git": "^1.92.0",
|
||||
"styled-components": "^3.2.6",
|
||||
"typescript": "^2.8.1",
|
||||
"vscode-css-languageserver-bin": "^1.2.1",
|
||||
|
@ -913,7 +757,7 @@
|
|||
"@types/react-transition-group": "2.0.6",
|
||||
"@types/react-virtualized": "^9.7.10",
|
||||
"@types/redux-batched-subscribe": "^0.1.2",
|
||||
"@types/redux-mock-store": "^0.0.13",
|
||||
"@types/redux-mock-store": "^1.0.0",
|
||||
"@types/rimraf": "^2.0.2",
|
||||
"@types/shelljs": "^0.7.7",
|
||||
"@types/sinon": "1.16.32",
|
||||
|
@ -948,7 +792,7 @@
|
|||
"innosetup-compiler": "5.5.9",
|
||||
"istanbul-api": "^1.2.1",
|
||||
"istanbul-lib-coverage": "^1.1.1",
|
||||
"jest": "^22.2.2",
|
||||
"jest": "^23.2.0",
|
||||
"jsdom": "11.0.0",
|
||||
"less": "2.7.1",
|
||||
"less-loader": "^4.1.0",
|
||||
|
@ -960,6 +804,7 @@
|
|||
"mkdirp": "0.5.1",
|
||||
"mocha": "3.1.2",
|
||||
"node-abi": "^2.4.1",
|
||||
"npm-run-all": "^4.1.3",
|
||||
"nyc": "^11.4.1",
|
||||
"oni-core-logging": "^1.0.0",
|
||||
"oni-release-downloader": "^0.0.10",
|
||||
|
@ -973,17 +818,17 @@
|
|||
"react-transition-group": "2.2.1",
|
||||
"react-virtualized": "^9.18.0",
|
||||
"redux": "3.7.2",
|
||||
"redux-mock-store": "^1.5.1",
|
||||
"redux-mock-store": "^1.5.3",
|
||||
"redux-observable": "0.17.0",
|
||||
"redux-thunk": "2.2.0",
|
||||
"remap-istanbul": "^0.10.1",
|
||||
"reselect": "3.0.1",
|
||||
"rxjs": "^5.5.8",
|
||||
"rxjs": "5.5.8",
|
||||
"sinon": "1.17.6",
|
||||
"spectron": "^3.8.0",
|
||||
"style-loader": "0.18.2",
|
||||
"sudo-prompt": "7.1.1",
|
||||
"ts-jest": "^22.0.4",
|
||||
"ts-jest": "^23.0.0",
|
||||
"ts-loader": "^4.2.0",
|
||||
"tslint": "5.9.1",
|
||||
"vscode-snippet-parser": "0.0.5",
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
import { mount, shallow } from "enzyme"
|
||||
import { shallowToJson } from "enzyme-to-json"
|
||||
import * as React from "react"
|
||||
|
||||
import { Branch, BranchNameContainer } from "./../browser/src/UI/components/VersionControl"
|
||||
|
||||
describe("<Branch />", () => {
|
||||
const diff = {
|
||||
insertions: 2,
|
||||
deletions: 8,
|
||||
files: null,
|
||||
}
|
||||
|
||||
it("Should render without crashing", () => {
|
||||
const wrapper = shallow(<Branch branch="test-branch" diff={diff} />)
|
||||
expect(wrapper.length).toEqual(1)
|
||||
})
|
||||
|
||||
it("Should render the correct branch name", () => {
|
||||
const wrapper = shallow(<Branch branch="test-branch" diff={diff} />)
|
||||
const name = wrapper
|
||||
.find(BranchNameContainer)
|
||||
.dive()
|
||||
.text()
|
||||
expect(name).toBe("test-branch < />")
|
||||
})
|
||||
it("should match last known snapshot unless we make a change", () => {
|
||||
const wrapper = shallow(<Branch branch="test-branch" diff={diff} />)
|
||||
expect(shallowToJson(wrapper)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("Should show the correct number of additions", () => {
|
||||
const wrapper = mount(<Branch branch="test-branch" diff={diff} />)
|
||||
const additions = wrapper.find("[data-test='addition-2']")
|
||||
expect(additions.find("span").text()).toEqual("2")
|
||||
})
|
||||
|
||||
it("Should show the correct number of deletions", () => {
|
||||
const wrapper = mount(<Branch branch="test-branch" diff={diff} />)
|
||||
const deletions = wrapper.find("[data-test='deletion-8']")
|
||||
expect(deletions.find("span").text()).toEqual("8")
|
||||
})
|
||||
|
||||
it("should render the correct icon for additions", () => {
|
||||
const wrapper = mount(<Branch branch="test-branch" diff={diff} />)
|
||||
const icon = wrapper.find("i.fa-plus-circle")
|
||||
expect(icon.length).toBe(1)
|
||||
})
|
||||
|
||||
it("should render the correct icon for deletions", () => {
|
||||
const wrapper = mount(<Branch branch="test-branch" diff={diff} />)
|
||||
const icon = wrapper.find("i.fa-minus-circle")
|
||||
expect(icon.length).toBe(1)
|
||||
})
|
||||
|
||||
it("Should not render an icon if there were no insertions", () => {
|
||||
const wrapper = mount(<Branch branch="test-branch" diff={{ ...diff, insertions: 0 }} />)
|
||||
const icon = wrapper.find("i.fa-plus-circle")
|
||||
expect(icon.length).toBe(0)
|
||||
})
|
||||
|
||||
it("Should not render an icon if there were no deletions", () => {
|
||||
const wrapper = mount(<Branch branch="test-branch" diff={{ ...diff, deletions: 0 }} />)
|
||||
const icon = wrapper.find("i.fa-minus-circle")
|
||||
expect(icon.length).toBe(0)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,94 @@
|
|||
import * as Oni from "oni-api"
|
||||
import { Event } from "oni-types"
|
||||
import * as React from "react"
|
||||
|
||||
import { Branch } from "../browser/src/UI/components/VersionControl"
|
||||
import {
|
||||
VersionControlManager,
|
||||
VersionControlProvider,
|
||||
} from "./../browser/src/Services/VersionControl"
|
||||
|
||||
import MockCommands from "./mocks/CommandManager"
|
||||
import { configuration as MockConfiguration } from "./mocks/Configuration"
|
||||
import MockEditorManager from "./mocks/EditorManager"
|
||||
import MockMenu from "./mocks/MenuManager"
|
||||
import MockNotifications from "./mocks/Notifications"
|
||||
import MockSidebar from "./mocks/Sidebar"
|
||||
import MockStatusbar, { mockStatusBarHide, mockStatusBarSetContents } from "./mocks/Statusbar"
|
||||
import MockWorkspace from "./mocks/Workspace"
|
||||
|
||||
jest.unmock("lodash")
|
||||
|
||||
const makePromise = (arg?: any) => Promise.resolve(arg)
|
||||
|
||||
const provider: VersionControlProvider = {
|
||||
name: "svn",
|
||||
onFileStatusChanged: new Event(),
|
||||
onBranchChanged: new Event(),
|
||||
onPluginActivated: new Event(),
|
||||
onPluginDeactivated: new Event(),
|
||||
onStagedFilesChanged: new Event(),
|
||||
isActivated: true,
|
||||
fetchBranchFromRemote: () => null,
|
||||
stageFile: () => null,
|
||||
changeBranch: () => null,
|
||||
getLocalBranches: () => makePromise(["branch1", "branch2"]),
|
||||
canHandleWorkspace: () => makePromise(true),
|
||||
getDiff: () => makePromise({}),
|
||||
activate: () => null,
|
||||
deactivate: () => null,
|
||||
getStatus: () => makePromise({}),
|
||||
getRoot: () => makePromise("/test/dir"),
|
||||
getBranch: () => makePromise("local"),
|
||||
}
|
||||
|
||||
describe("Version Control Manager tests", () => {
|
||||
let vcsManager: VersionControlManager
|
||||
beforeEach(() => {
|
||||
vcsManager = new VersionControlManager(
|
||||
new MockWorkspace(),
|
||||
new MockEditorManager(),
|
||||
new MockStatusbar(),
|
||||
new MockMenu(),
|
||||
new MockCommands(),
|
||||
new MockSidebar(),
|
||||
new MockNotifications(),
|
||||
MockConfiguration,
|
||||
)
|
||||
vcsManager.registerProvider(provider)
|
||||
})
|
||||
|
||||
it("Should register a vcs provider", () => {
|
||||
expect(vcsManager.providers.size).toBe(1)
|
||||
})
|
||||
|
||||
it("Should register the provider details", () => {
|
||||
expect(vcsManager.activeProvider.name).toBe("svn")
|
||||
})
|
||||
|
||||
it("should correctly deregister a provider", () => {
|
||||
vcsManager.deactivateProvider()
|
||||
expect(vcsManager.activeProvider).toBeFalsy()
|
||||
})
|
||||
|
||||
it("Should correctly hide the status bar item if the dir cannot handle the workspace", () => {
|
||||
provider.canHandleWorkspace = async () => makePromise(false)
|
||||
vcsManager.registerProvider(provider)
|
||||
expect(mockStatusBarHide.mock.calls.length).toBe(1)
|
||||
})
|
||||
|
||||
it("should return the correct branch", async () => {
|
||||
const branch = await provider.getBranch()
|
||||
expect(branch).toBe("local")
|
||||
})
|
||||
|
||||
it("Should return the correct local branches", async () => {
|
||||
const localBranches = await provider.getLocalBranches()
|
||||
expect(localBranches).toEqual(expect.arrayContaining(["branch1", "branch2"]))
|
||||
})
|
||||
|
||||
it("should set the contents of the statusbar correctly", () => {
|
||||
const branch = <Branch diff={{} as any} branch="local" />
|
||||
expect(mockStatusBarSetContents.mock.calls[0][0]).toEqual(branch)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,69 @@
|
|||
import * as Oni from "oni-api"
|
||||
import { Event } from "oni-types"
|
||||
|
||||
import { VersionControlProvider } from "../browser/src/Services/VersionControl"
|
||||
import VersionControlPane from "./../browser/src/Services/VersionControl/VersionControlPane"
|
||||
import store, {
|
||||
DefaultState,
|
||||
VersionControlState,
|
||||
} from "./../browser/src/Services/VersionControl/VersionControlStore"
|
||||
import MockEditorManager from "./mocks/EditorManager"
|
||||
import MockWorkspace from "./mocks/Workspace"
|
||||
|
||||
jest.mock("lodash/capitalize", (str: string) => str)
|
||||
jest.mock("./../browser/src/Services/VersionControl/VersionControlView", () => "VersionControlView")
|
||||
|
||||
const makePromise = (arg?: any) => Promise.resolve(arg)
|
||||
|
||||
const provider: VersionControlProvider = {
|
||||
name: "git",
|
||||
onFileStatusChanged: new Event(),
|
||||
onBranchChanged: new Event(),
|
||||
onPluginActivated: new Event(),
|
||||
onPluginDeactivated: new Event(),
|
||||
onStagedFilesChanged: new Event(),
|
||||
isActivated: true,
|
||||
fetchBranchFromRemote: () => null,
|
||||
stageFile: () => null,
|
||||
changeBranch: () => null,
|
||||
getLocalBranches: () => makePromise(["branch1", "branch2"]),
|
||||
canHandleWorkspace: () => makePromise(true),
|
||||
getDiff: () => makePromise({}),
|
||||
activate: () => null,
|
||||
deactivate: () => null,
|
||||
getStatus: () =>
|
||||
makePromise({
|
||||
currentBranch: "master",
|
||||
}),
|
||||
getRoot: () => makePromise("/test/dir"),
|
||||
getBranch: () => makePromise("local"),
|
||||
}
|
||||
|
||||
describe("Version Control pane tests", () => {
|
||||
const mockManager = new MockEditorManager()
|
||||
const mockWorkspace = new MockWorkspace()
|
||||
const vcsStore = store
|
||||
const vcsPane = new VersionControlPane(
|
||||
mockManager,
|
||||
mockWorkspace,
|
||||
provider,
|
||||
args => null,
|
||||
store,
|
||||
)
|
||||
|
||||
it("Should create a new version control pane", () => {
|
||||
expect(vcsPane.id).toBe("oni.sidebar.vcs")
|
||||
})
|
||||
|
||||
it("get status should return the value expected", async () => {
|
||||
const result = await vcsPane.getStatus()
|
||||
if (result) {
|
||||
expect(result.currentBranch).toEqual("master")
|
||||
}
|
||||
})
|
||||
it("Correctly update the store", async () => {
|
||||
await vcsPane.getStatus()
|
||||
const state = store.getState()
|
||||
expect(state.status.currentBranch).toBe("master")
|
||||
})
|
||||
})
|
|
@ -0,0 +1,39 @@
|
|||
import { Store } from "redux"
|
||||
|
||||
import store from "./../browser/src/Services/VersionControl/VersionControlStore"
|
||||
|
||||
describe("Version control reducer test", () => {
|
||||
const vcsStore = store
|
||||
|
||||
it("Should correctly update the store with the vcs status", () => {
|
||||
const status = {
|
||||
currentBranch: "master",
|
||||
staged: ["/test.txt"],
|
||||
conflicted: [],
|
||||
created: [],
|
||||
modified: [],
|
||||
remoteTrackingBranch: "origin/master",
|
||||
deleted: [],
|
||||
untracked: [],
|
||||
ahead: null,
|
||||
behind: null,
|
||||
}
|
||||
|
||||
vcsStore.dispatch({ type: "STATUS", payload: { status } })
|
||||
const state = vcsStore.getState()
|
||||
expect(state.status.currentBranch).toBe("master")
|
||||
expect(state.status.staged[0]).toBe("/test.txt")
|
||||
})
|
||||
|
||||
it("should correctly update the focus state", () => {
|
||||
vcsStore.dispatch({ type: "ENTER" })
|
||||
const state = store.getState()
|
||||
expect(state.hasFocus).toBe(true)
|
||||
})
|
||||
|
||||
it("Should correctly register an error", () => {
|
||||
vcsStore.dispatch({ type: "ERROR" })
|
||||
const state = store.getState()
|
||||
expect(state.hasError).toBe(true)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,104 @@
|
|||
import { shallow } from "enzyme"
|
||||
import { shallowToJson } from "enzyme-to-json"
|
||||
import * as React from "react"
|
||||
|
||||
import {
|
||||
DefaultState,
|
||||
VersionControlState,
|
||||
} from "./../browser/src/Services/VersionControl/VersionControlStore"
|
||||
import {
|
||||
GitStatus,
|
||||
SectionTitle,
|
||||
VersionControlView,
|
||||
} from "./../browser/src/Services/VersionControl/VersionControlView"
|
||||
|
||||
const noop = () => ({})
|
||||
|
||||
jest.mock("./../browser/src/neovim/SharedNeovimInstance", () => ({
|
||||
getInstance: () => ({
|
||||
bindToMenu: () => ({
|
||||
setItems: jest.fn(),
|
||||
onCursorMoved: {
|
||||
subscribe: jest.fn(),
|
||||
},
|
||||
}),
|
||||
}),
|
||||
}))
|
||||
|
||||
const makePromise = (arg?: any) => Promise.resolve(arg)
|
||||
|
||||
jest.mock("../browser/src/UI/components/Sneakable", () => {
|
||||
const React = require("react") // tslint:disable-line
|
||||
return { Sneakable: () => <div /> }
|
||||
})
|
||||
|
||||
describe("<VersionControlView />", () => {
|
||||
const state = { ...DefaultState, activated: true, hasFocus: true }
|
||||
const container = shallow(<VersionControlView {...state} getStatus={() => makePromise({})} />)
|
||||
it("renders without crashing", () => {
|
||||
expect(container.length).toBe(1)
|
||||
})
|
||||
|
||||
it("should render an untracked, staged and modified section", () => {
|
||||
const sections = container.dive().find(GitStatus).length
|
||||
expect(sections).toBe(3)
|
||||
})
|
||||
|
||||
it("shouldn't show a section if it has no content", () => {
|
||||
const wrapper = shallow(
|
||||
<GitStatus
|
||||
onClick={noop}
|
||||
toggleVisibility={noop}
|
||||
visibility={true}
|
||||
titleId="modified"
|
||||
selectedId="file1"
|
||||
icon="M"
|
||||
files={null}
|
||||
/>,
|
||||
)
|
||||
expect(wrapper.find(SectionTitle).length).toBe(0)
|
||||
})
|
||||
|
||||
it("should match the last recorded snapshot unless a change was made", () => {
|
||||
const wrapper = shallow(
|
||||
<GitStatus
|
||||
titleId="modified"
|
||||
visibility={true}
|
||||
toggleVisibility={noop}
|
||||
onClick={noop}
|
||||
selectedId="file1"
|
||||
icon="M"
|
||||
files={["test1", "test2"]}
|
||||
/>,
|
||||
)
|
||||
expect(shallowToJson(wrapper)).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("should render the correct number of modified files from the store in the correct section from of the pane", () => {
|
||||
const stateCopy = {
|
||||
...DefaultState,
|
||||
activated: true,
|
||||
hasFocus: true,
|
||||
status: {
|
||||
currentBranch: null,
|
||||
staged: [],
|
||||
conflicted: [],
|
||||
created: [],
|
||||
modified: ["test1", "test2"],
|
||||
remoteTrackingBranch: null,
|
||||
deleted: [],
|
||||
untracked: [],
|
||||
ahead: null,
|
||||
behind: null,
|
||||
},
|
||||
}
|
||||
|
||||
const statusComponent = shallow(
|
||||
<VersionControlView {...stateCopy} getStatus={() => makePromise({})} />,
|
||||
)
|
||||
.dive()
|
||||
.findWhere(component => component.prop("titleId") === "modified")
|
||||
|
||||
expect(statusComponent.prop("files").length).toBe(2)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,18 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Branch /> should match last known snapshot unless we make a change 1`] = `
|
||||
<styled.div>
|
||||
<styled.span>
|
||||
<Icon
|
||||
name="code-fork"
|
||||
/>
|
||||
<styled.span>
|
||||
test-branch
|
||||
<Component
|
||||
deletions={8}
|
||||
insertions={2}
|
||||
/>
|
||||
</styled.span>
|
||||
</styled.span>
|
||||
</styled.div>
|
||||
`;
|
|
@ -0,0 +1,57 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<VersionControlView /> should match the last recorded snapshot unless a change was made 1`] = `
|
||||
<div>
|
||||
<styled.div
|
||||
data-test="modified-2"
|
||||
isSelected={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Caret
|
||||
active={true}
|
||||
/>
|
||||
<styled.h4>
|
||||
MODIFIED
|
||||
</styled.h4>
|
||||
<strong>
|
||||
2
|
||||
</strong>
|
||||
</styled.div>
|
||||
<Sneakable
|
||||
callback={[Function]}
|
||||
key="test1"
|
||||
>
|
||||
<styled.div
|
||||
isSelected={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
<styled.div>
|
||||
<Icon
|
||||
name="M"
|
||||
/>
|
||||
<styled.span>
|
||||
test1
|
||||
</styled.span>
|
||||
</styled.div>
|
||||
</styled.div>
|
||||
</Sneakable>
|
||||
<Sneakable
|
||||
callback={[Function]}
|
||||
key="test2"
|
||||
>
|
||||
<styled.div
|
||||
isSelected={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
<styled.div>
|
||||
<Icon
|
||||
name="M"
|
||||
/>
|
||||
<styled.span>
|
||||
test2
|
||||
</styled.span>
|
||||
</styled.div>
|
||||
</styled.div>
|
||||
</Sneakable>
|
||||
</div>
|
||||
`;
|
|
@ -1,9 +1,9 @@
|
|||
// tslint:disable
|
||||
import * as Enzyme from "enzyme"
|
||||
import Adapter from "enzyme-adapter-react-16"
|
||||
import { configure } from "enzyme"
|
||||
import * as Adapter from "enzyme-adapter-react-16"
|
||||
|
||||
// React 16 Enzyme adapter
|
||||
Enzyme.configure({ adapter: new Adapter() })
|
||||
configure({ adapter: new Adapter() })
|
||||
|
||||
// Make Enzyme functions available in all test files without importing
|
||||
// ;(global as any).shallow = shallow
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { CommandManager } from "./../../browser/src/Services/CommandManager"
|
||||
|
||||
export const mockRegisterCommands = jest.fn()
|
||||
const MockCommands = jest.fn<CommandManager>().mockImplementation(() => ({
|
||||
registerCommand: mockRegisterCommands,
|
||||
}))
|
||||
|
||||
export default MockCommands
|
|
@ -1,5 +1,12 @@
|
|||
const Configuration = jest.fn().mockImplementation(() => {
|
||||
import * as Oni from "oni-api"
|
||||
|
||||
const Configuration = jest.fn<Oni.Configuration>().mockImplementation(() => {
|
||||
return {
|
||||
onConfigurationChanged() {
|
||||
return {
|
||||
subscribe: jest.fn(),
|
||||
}
|
||||
},
|
||||
notifyListeners: jest.fn(),
|
||||
updateConfig: jest.fn(),
|
||||
getValue: jest.fn(),
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { Event } from "oni-types"
|
||||
import { EditorManager } from "./../../browser/src/Services/EditorManager"
|
||||
|
||||
const _onBufferSaved = new Event<any>("Test:ActiveEditor-BufferSaved")
|
||||
const _onBufferEnter = new Event<any>("Test:ActiveEditor-BufferEnter")
|
||||
const MockEditorManager = jest.fn<EditorManager>().mockImplementation(() => ({
|
||||
activeEditor: {
|
||||
activeBuffer: {
|
||||
filePath: "test.txt",
|
||||
},
|
||||
onBufferEnter: _onBufferEnter,
|
||||
onBufferSaved: _onBufferSaved,
|
||||
},
|
||||
}))
|
||||
|
||||
export default MockEditorManager
|
|
@ -0,0 +1,18 @@
|
|||
import { MenuManager } from "./../../browser/src/Services/Menu"
|
||||
|
||||
export const mockMenuShow = jest.fn()
|
||||
const MockMenu = jest.fn<MenuManager>().mockImplementation(() => ({
|
||||
create() {
|
||||
return {
|
||||
show: mockMenuShow,
|
||||
setItems(items: {}) {
|
||||
return items
|
||||
},
|
||||
onItemSelected: {
|
||||
subscribe: jest.fn(),
|
||||
},
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
export default MockMenu
|
|
@ -0,0 +1,4 @@
|
|||
import { Notifications } from "./../../browser/src/Services/Notifications"
|
||||
|
||||
const MockNotifications = jest.fn<Notifications>().mockImplementation(() => ({}))
|
||||
export default MockNotifications
|
|
@ -0,0 +1,11 @@
|
|||
import { SidebarManager } from "./../../browser/src/Services/Sidebar"
|
||||
|
||||
const MockSidebar = jest.fn<SidebarManager>().mockImplementation(() => ({
|
||||
entries: [
|
||||
{
|
||||
id: "git-vcs",
|
||||
},
|
||||
],
|
||||
}))
|
||||
|
||||
export default MockSidebar
|
|
@ -0,0 +1,19 @@
|
|||
import * as Oni from "oni-api"
|
||||
|
||||
export const mockStatusBarShow = jest.fn()
|
||||
export const mockStatusBarHide = jest.fn()
|
||||
export const mockStatusBarSetContents = jest.fn()
|
||||
export const mockStatusBarDisposal = jest.fn()
|
||||
|
||||
const MockStatusbar = jest.fn<Oni.StatusBar>().mockImplementation(() => ({
|
||||
createItem(alignment: number, vcsId: string) {
|
||||
return {
|
||||
show: mockStatusBarShow,
|
||||
hide: mockStatusBarHide,
|
||||
setContents: mockStatusBarSetContents,
|
||||
dispose: mockStatusBarDisposal,
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
export default MockStatusbar
|
|
@ -0,0 +1,13 @@
|
|||
import { IWorkspace, Workspace } from "./../../browser/src/Services/Workspace"
|
||||
|
||||
const MockWorkspace = jest.fn<IWorkspace>().mockImplementation(() => ({
|
||||
activeDirectory: "test/dir",
|
||||
onDirectoryChanged: {
|
||||
subscribe: jest.fn(),
|
||||
},
|
||||
onFocusGained: {
|
||||
subscribe: jest.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
export default MockWorkspace
|
|
@ -20,14 +20,9 @@
|
|||
"suppressImplicitAnyIndexErrors": true,
|
||||
"target": "es2015",
|
||||
"sourceMap": true,
|
||||
"inlineSourceMap": true,
|
||||
"types": ["jest", "electron", "react", "webgl2"]
|
||||
},
|
||||
"include": [
|
||||
"**/*.tsx",
|
||||
"**/*.ts",
|
||||
"../@types/**/*.d.ts",
|
||||
"../browser/**/*.ts",
|
||||
"../browser/**/*.tsx"
|
||||
],
|
||||
"include": ["**/*.tsx", "**/*.ts", "../browser/**/*.ts", "../browser/**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const { promisify } = require("util")
|
||||
const fsStat = promisify(fs.stat)
|
||||
|
||||
const activate = Oni => {
|
||||
const React = Oni.dependencies.React
|
||||
let isLoaded = false
|
||||
try {
|
||||
const updateBranchIndicator = async evt => {
|
||||
if (!evt) {
|
||||
return
|
||||
}
|
||||
const filePath = evt.filePath || evt.bufferFullPath
|
||||
const gitId = "oni.status.git"
|
||||
const gitBranchIndicator = Oni.statusBar.createItem(1, gitId)
|
||||
|
||||
isLoaded = true
|
||||
let dir
|
||||
try {
|
||||
const isDir = await Oni.workspace.pathIsDir(filePath)
|
||||
const dir = isDir ? filePath : path.dirname(filePath)
|
||||
let branchName
|
||||
try {
|
||||
branchName = await Oni.services.git.getBranch(dir)
|
||||
} catch (e) {
|
||||
gitBranchIndicator.hide()
|
||||
return
|
||||
// return console.warn('[Oni.plugin.git]: No branch name found', e);
|
||||
// branchName = 'Not a Git Repo';
|
||||
}
|
||||
|
||||
const props = {
|
||||
style: {
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
}
|
||||
|
||||
const branchContainerProps = {
|
||||
style: {
|
||||
minWidth: "10px",
|
||||
textAlign: "center",
|
||||
padding: "2px 4px 0 0",
|
||||
},
|
||||
}
|
||||
|
||||
const branchIcon = Oni.ui.createIcon({
|
||||
name: "code-fork",
|
||||
size: Oni.ui.iconSize.Default,
|
||||
})
|
||||
|
||||
const branchContainer = React.createElement(
|
||||
"span",
|
||||
branchContainerProps,
|
||||
branchIcon,
|
||||
)
|
||||
|
||||
const branchNameContainer = React.createElement(
|
||||
"div",
|
||||
{ width: "100%" },
|
||||
" " + branchName,
|
||||
)
|
||||
|
||||
const gitBranch = React.createElement(
|
||||
"div",
|
||||
props,
|
||||
branchContainer,
|
||||
branchNameContainer,
|
||||
)
|
||||
|
||||
gitBranchIndicator.setContents(gitBranch)
|
||||
gitBranchIndicator.show()
|
||||
} catch (e) {
|
||||
console.log("[Oni.plugin.git]: ", e)
|
||||
return gitBranchIndicator.hide()
|
||||
}
|
||||
}
|
||||
|
||||
if (!isLoaded) {
|
||||
updateBranchIndicator(Oni.editors.activeEditor.activeBuffer)
|
||||
}
|
||||
|
||||
Oni.editors.activeEditor.onBufferEnter.subscribe(
|
||||
async evt => await updateBranchIndicator(evt),
|
||||
)
|
||||
Oni.workspace.onFocusGained.subscribe(async buffer => await updateBranchIndicator(buffer))
|
||||
} catch (e) {
|
||||
console.warn("[Oni.plugin.git] ERROR", e)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
activate,
|
||||
}
|
|
@ -1,14 +1,25 @@
|
|||
{
|
||||
"name": "oni-plugin-git",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"main": "lib/index.js",
|
||||
"engines": {
|
||||
"oni": "^0.2.6"
|
||||
},
|
||||
"scripts": {},
|
||||
"oni": {
|
||||
"supportedFileTypes": ["*"]
|
||||
"scripts": {
|
||||
"build": "rimraf lib && tsc",
|
||||
"test": "tsc -p tsconfig.test.json && mocha --recursive ./lib_test/test"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {}
|
||||
"oni": {
|
||||
"supportedFileTypes": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"oni-api": "^0.0.46",
|
||||
"oni-types": "^0.0.8",
|
||||
"simple-git": "^1.96.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^2.9.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
/**
|
||||
* Git.ts
|
||||
*
|
||||
*/
|
||||
|
||||
import * as Oni from "oni-api"
|
||||
import { Event, IEvent } from "oni-types"
|
||||
import * as GitP from "simple-git/promise"
|
||||
|
||||
import * as VCS from "./vcs" // TODO: import from oni-api
|
||||
|
||||
interface FileSummary {
|
||||
index: string
|
||||
path: string
|
||||
working_dir: string
|
||||
}
|
||||
|
||||
export class GitVersionControlProvider implements VCS.VersionControlProvider {
|
||||
private readonly _name = "git"
|
||||
private _onPluginActivated = new Event<void>("Oni::VCSPluginActivated")
|
||||
private _onPluginDeactivated = new Event<void>("Oni::VCSPluginDeactivated")
|
||||
private _onBranchChange = new Event<VCS.BranchChangedEvent>("Oni::VCSBranchChanged")
|
||||
private _onStagedFilesChanged = new Event<VCS.StagedFilesChangedEvent>(
|
||||
"Oni::VCSStagedFilesChanged",
|
||||
)
|
||||
private _onFileStatusChanged = new Event<VCS.FileStatusChangedEvent>(
|
||||
"Oni::VCSFilesStatusChanged",
|
||||
)
|
||||
private _isActivated = false
|
||||
private _projectRoot: string
|
||||
|
||||
constructor(private _oni: Oni.Plugin.Api, private _git = GitP) {
|
||||
this._projectRoot = this._oni.workspace.activeWorkspace
|
||||
this._oni.workspace.onDirectoryChanged.subscribe(workspace => {
|
||||
this._projectRoot = workspace
|
||||
})
|
||||
}
|
||||
|
||||
get onBranchChanged(): IEvent<VCS.BranchChangedEvent> {
|
||||
return this._onBranchChange
|
||||
}
|
||||
|
||||
get onFileStatusChanged(): IEvent<VCS.FileStatusChangedEvent> {
|
||||
return this._onFileStatusChanged
|
||||
}
|
||||
|
||||
get onStagedFilesChanged(): IEvent<VCS.StagedFilesChangedEvent> {
|
||||
return this._onStagedFilesChanged
|
||||
}
|
||||
|
||||
get onPluginActivated(): IEvent<void> {
|
||||
return this._onPluginActivated
|
||||
}
|
||||
|
||||
get onPluginDeactivated(): IEvent<void> {
|
||||
return this._onPluginDeactivated
|
||||
}
|
||||
|
||||
get isActivated(): boolean {
|
||||
return this._isActivated
|
||||
}
|
||||
|
||||
get name(): VCS.SupportedProviders {
|
||||
return this._name
|
||||
}
|
||||
|
||||
public activate() {
|
||||
this._isActivated = true
|
||||
this._onPluginActivated.dispatch()
|
||||
}
|
||||
|
||||
public deactivate() {
|
||||
this._isActivated = false
|
||||
this._onPluginDeactivated.dispatch()
|
||||
}
|
||||
|
||||
public async canHandleWorkspace(dir?: string) {
|
||||
try {
|
||||
return this._git(this._projectRoot)
|
||||
.silent()
|
||||
.checkIsRepo()
|
||||
} catch (e) {
|
||||
this._oni.log.warn(
|
||||
`Git provider was unable to check if this directory is a repository because ${
|
||||
e.message
|
||||
}`,
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public async getRoot() {
|
||||
try {
|
||||
return this._git(this._projectRoot).revparse(["--show-toplevel"])
|
||||
} catch (e) {
|
||||
this._oni.log.warn(`Git provider unable to find vcs root due to ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
public getStatus = async (): Promise<VCS.StatusResult | void> => {
|
||||
try {
|
||||
const status = await this._git(this._projectRoot).status()
|
||||
const { modified, staged } = this._getModifiedAndStaged(status.files)
|
||||
return {
|
||||
staged,
|
||||
modified,
|
||||
ahead: status.ahead,
|
||||
behind: status.behind,
|
||||
created: status.created,
|
||||
deleted: status.deleted,
|
||||
currentBranch: status.current,
|
||||
conflicted: status.conflicted,
|
||||
untracked: status.not_added,
|
||||
remoteTrackingBranch: status.tracking,
|
||||
}
|
||||
} catch (error) {
|
||||
this._oni.log.warn(
|
||||
`Git provider unable to get current status because of: ${error.message}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public getDiff = async () => {
|
||||
try {
|
||||
return this._git(this._projectRoot).diffSummary()
|
||||
} catch (e) {
|
||||
const error = `Git provider unable to get current status because of: ${e.message}`
|
||||
this._oni.log.warn(error)
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
public stageFile = async (file: string, dir?: string) => {
|
||||
try {
|
||||
await this._git(this._projectRoot).add(file)
|
||||
this._onStagedFilesChanged.dispatch(file)
|
||||
this._onFileStatusChanged.dispatch({ path: file, status: "staged" })
|
||||
} catch (e) {
|
||||
const error = `Git provider unable to add ${file} because ${e.message}`
|
||||
this._oni.log.warn(error)
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
public fetchBranchFromRemote = async ({
|
||||
branch,
|
||||
currentDir,
|
||||
remote = null,
|
||||
}: {
|
||||
branch: string
|
||||
remote: string
|
||||
currentDir: string
|
||||
}) => {
|
||||
try {
|
||||
return this._git(this._projectRoot).fetch(remote, branch)
|
||||
} catch (e) {
|
||||
const error = `Git provider unable to fetch branch because of: ${e.message}`
|
||||
this._oni.log.warn(error)
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
public getLocalBranches = async () => {
|
||||
try {
|
||||
return this._git(this._projectRoot).branchLocal()
|
||||
} catch (e) {
|
||||
const error = `Git provider unable to get local branches because of: ${e.message}`
|
||||
this._oni.log.warn(error)
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
public getBranch = async (): Promise<string | void> => {
|
||||
try {
|
||||
const status = await this._git(this._projectRoot).status()
|
||||
return status.current
|
||||
} catch (e) {
|
||||
const error = `Git Provider was unable to get current status because of: ${e.message}`
|
||||
this._oni.log.warn(error)
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
public async changeBranch(targetBranch: string) {
|
||||
try {
|
||||
await this._git(this._projectRoot).checkout(targetBranch)
|
||||
this._onBranchChange.dispatch(targetBranch)
|
||||
} catch (e) {
|
||||
const error = `Git Provider was unable change branch because of: ${e.message}`
|
||||
this._oni.log.warn(error)
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
private _isStaged = (file: FileSummary) => {
|
||||
const GitPIndicators = ["M", "A"]
|
||||
return GitPIndicators.some(status => file.index.includes(status))
|
||||
}
|
||||
|
||||
private _getModifiedAndStaged(files: FileSummary[]): { modified: string[]; staged: string[] } {
|
||||
return files.reduce(
|
||||
(acc, file) => {
|
||||
if (file.working_dir === "M") {
|
||||
acc.modified.push(file.path)
|
||||
} else if (this._isStaged(file)) {
|
||||
acc.staged.push(file.path)
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ modified: [], staged: [] },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const activate = async oni => {
|
||||
const provider = new GitVersionControlProvider(oni)
|
||||
await oni.services.vcs.registerProvider(provider)
|
||||
|
||||
return provider
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
import { IEvent } from "oni-types"
|
||||
import { BranchSummary, FetchResult } from "simple-git/promise"
|
||||
|
||||
export type BranchChangedEvent = string
|
||||
export type StagedFilesChangedEvent = string
|
||||
export interface FileStatusChangedEvent {
|
||||
path: string
|
||||
status: "staged"
|
||||
}
|
||||
|
||||
export interface StatusResult {
|
||||
ahead: number
|
||||
behind: number
|
||||
currentBranch: string
|
||||
modified: string[]
|
||||
staged: string[]
|
||||
conflicted: string[]
|
||||
created: string[]
|
||||
deleted: string[]
|
||||
untracked: string[]
|
||||
remoteTrackingBranch: string
|
||||
}
|
||||
|
||||
export interface VersionControlProvider {
|
||||
// Events
|
||||
onFileStatusChanged: IEvent<FileStatusChangedEvent>
|
||||
onStagedFilesChanged: IEvent<StagedFilesChangedEvent>
|
||||
onBranchChanged: IEvent<BranchChangedEvent>
|
||||
onPluginActivated: IEvent<void>
|
||||
onPluginDeactivated: IEvent<void>
|
||||
|
||||
name: SupportedProviders
|
||||
isActivated: boolean
|
||||
deactivate(): void
|
||||
activate(): void
|
||||
canHandleWorkspace(dir?: string): Promise<boolean>
|
||||
getStatus(): Promise<StatusResult | void>
|
||||
getRoot(): Promise<string | void>
|
||||
getDiff(): Promise<Diff | void>
|
||||
getBranch(): Promise<string | void>
|
||||
getLocalBranches(): Promise<BranchSummary | void>
|
||||
changeBranch(branch: string): Promise<void>
|
||||
stageFile(file: string, projectRoot?: string): Promise<void>
|
||||
fetchBranchFromRemote(args: {
|
||||
branch: string
|
||||
origin?: string
|
||||
currentDir: string
|
||||
}): Promise<FetchResult>
|
||||
}
|
||||
|
||||
export interface DiffResultTextFile {
|
||||
file: string
|
||||
changes: number
|
||||
insertions: number
|
||||
deletions: number
|
||||
binary: boolean
|
||||
}
|
||||
|
||||
export interface DiffResultBinaryFile {
|
||||
file: string
|
||||
before: number
|
||||
after: number
|
||||
binary: boolean
|
||||
}
|
||||
|
||||
export interface Diff {
|
||||
files: Array<DiffResultTextFile | DiffResultBinaryFile>
|
||||
insertions: number
|
||||
deletions: number
|
||||
}
|
||||
|
||||
export type Summary = StatusResult
|
||||
export type SupportedProviders = "git" | "svn"
|
||||
export default VersionControlProvider
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"preserveConstEnums": true,
|
||||
"outDir": "./lib",
|
||||
"jsx": "react",
|
||||
"lib": ["dom", "es2017"],
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"target": "es2015",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
Loading…
Reference in New Issue