Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration with VS Live Share #232

Merged
merged 1 commit into from
Nov 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"outFiles": [
"${workspaceRoot}/out/src/**/*.js"
],
"preLaunchTask": "npm"
"preLaunchTask": "build"
},
{
"name": "Launch Tests",
Expand All @@ -31,7 +31,7 @@
"outFiles": [
"${workspaceRoot}/out/test/**/*.js"
],
"preLaunchTask": "npm"
"preLaunchTask": "build"
}
]
}
46 changes: 27 additions & 19 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,31 @@

// A task runner that calls a custom npm script that compiles the extension.
{
"version": "0.1.0",

// we want to run npm
"command": "npm",

// the command is a shell script
"isShellCommand": true,

// show the output window only if unrecognized errors occur.
"showOutput": "silent",

// we run the custom script "compile" as defined in package.json
"args": ["run", "compile", "--loglevel", "silent"],

// The tsc compiler is started in watching mode
"isBackground": true,

// use the standard tsc in watch mode problem matcher to find compile problems in the output.
"problemMatcher": "$tsc-watch"
"version": "2.0.0",
"tasks": [
{
"label": "build",
// the command is a shell script
"type": "shell",
// show the output window only if unrecognized errors occur.
"presentation": {
"reveal": "silent",
"focus": false,
"panel": "shared",
},
// we run the custom script "compile" as defined in package.json
"command": "npm",
"args": ["run", "compile", "--loglevel", "silent"],
// The tsc compiler is started in watching mode
"isBackground": true,
// use the standard tsc in watch mode problem matcher to find compile problems in the output.
"problemMatcher": [
"$tsc-watch"
],
"group": {
"kind": "build",
"isDefault": true
},
}
]
}
4 changes: 2 additions & 2 deletions docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@
```js
{
"liveServer.settings.mount:" [
["/", "/path1"]
["/", "/path2"]
["/", "/path1"],
["/", "/path2"],
["/root", "/dist"]
]
}
Expand Down
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,8 @@
"http-shutdown": "^1.2.0",
"ips": "^2.1.3",
"live-server": "file:lib\\live-server",
"opn": "^5.3.0"
"opn": "^5.3.0",
"vsls": "^0.3.896"
},
"announcement": {
"onVersion": "5.0.0",
Expand Down
18 changes: 18 additions & 0 deletions src/IAppModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

import { Event } from 'vscode';

export interface GoLiveEvent {
readonly runningPort: number;
readonly pathUri?: string;
}

export interface GoOfflineEvent {
readonly runningPort: number;
}

export interface IAppModel {
readonly runningPort: number;
readonly onDidGoLive: Event<GoLiveEvent>;
readonly onDidGoOffline: Event<GoOfflineEvent>;
}
72 changes: 72 additions & 0 deletions src/LiveShareHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
'use strict';

import * as vscode from 'vscode';
import * as vsls from 'vsls/vscode';
import { IAppModel, GoLiveEvent, GoOfflineEvent } from './IAppModel';

/**
* Manages state of a live server shared via VS Live Share.
* Caches the live server path and starts/stops sharing in response to Live Share session events.
*/
export class LiveShareHelper implements vscode.Disposable {
private liveshare: vsls.LiveShare | undefined;
private activeHostSession: vsls.Session | undefined;
private livePathUri: string;

private deferredWork: Promise<void>;
private sharedServer: vscode.Disposable;

constructor(private readonly appModel: IAppModel) {
this.appModel.onDidGoLive(async (e: GoLiveEvent) => {
// cache the current live server browse url
this.livePathUri = e.pathUri;
await this.shareLiveServer();
});
this.appModel.onDidGoOffline((e: GoOfflineEvent) => {
// reset the live server cached path
this.livePathUri = null;
if (this.activeHostSession && this.sharedServer) {
// will un-share the server
this.sharedServer.dispose();
this.sharedServer = null;
}
});
this.deferredWork = vsls.getApi().then(api => {
if (api) { // if Live Share is available (installed)
this.ensureInitialized(api);
}
});
}

async dispose() {
await this.deferredWork;
}

private ensureInitialized(api: vsls.LiveShare) {
this.liveshare = api;
if (this.liveshare.session && this.liveshare.session.role === vsls.Role.Host) {
this.activeHostSession = this.liveshare.session;
}
this.liveshare.onDidChangeSession(async (e: vsls.SessionChangeEvent) => {
if (e.session.role === vsls.Role.Host) {
// active sharing collaboration session
this.activeHostSession = e.session;
await this.shareLiveServer();
} else {
// any other session state, including joined as a guest
this.activeHostSession = null;
}
});
}

private async shareLiveServer() {
if (this.activeHostSession && this.livePathUri) {
// only share the server when we're live and VS Live Share session is active
this.sharedServer = await this.liveshare.shareServer({
port: this.appModel.runningPort,
displayName: 'Live Server',
browseUrl: `http://localhost:${this.appModel.runningPort}/${this.livePathUri.replace(/\\/gi, '/')}`
});
}
}
}
31 changes: 26 additions & 5 deletions src/appModel.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,47 @@
'use strict';

import { window, workspace } from 'vscode';
import { window, workspace, Event, EventEmitter } from 'vscode';

import { LiveServerHelper } from './LiveServerHelper';
import { StatusbarUi } from './StatusbarUi';
import { Config } from './Config';
import { Helper, SUPPRORTED_EXT } from './Helper';
import { workspaceResolver, setOrChangeWorkspace } from './workspaceResolver';
import { IAppModel, GoLiveEvent, GoOfflineEvent } from './IAppModel';
import { LiveShareHelper } from './LiveShareHelper';

import * as opn from 'opn';
import * as ips from 'ips';

export class AppModel {
export class AppModel implements IAppModel {

private IsServerRunning: boolean;
private IsStaging: boolean;
private LiveServerInstance;
private runningPort: number;
private localIps: any;
private previousWorkspacePath: string;

private readonly goLiveEvent = new EventEmitter<GoLiveEvent>();
private readonly goOfflineEvent = new EventEmitter<GoOfflineEvent>();
private readonly liveShareHelper: LiveShareHelper;

public runningPort: number;

public get onDidGoLive(): Event<GoLiveEvent> {
return this.goLiveEvent.event;
}
public get onDidGoOffline(): Event<GoOfflineEvent> {
return this.goOfflineEvent.event;
}

constructor() {
const _ips = ips();
this.localIps = _ips.local ? _ips.local : Config.getHost;
this.IsServerRunning = false;
this.runningPort = null;

this.liveShareHelper = new LiveShareHelper(this);

this.haveAnySupportedFile().then(() => {
StatusbarUi.Init();
});
Expand All @@ -50,9 +66,11 @@ export class AppModel {
const pathInfos = Helper.testPathWithRoot(workspacePath);

if (this.IsServerRunning) {
const relativePath = Helper.getSubPath(pathInfos.rootPath, openedDocUri) || '';
this.goLiveEvent.fire({ runningPort: this.runningPort, pathUri: relativePath });
return this.openBrowser(
this.runningPort,
Helper.getSubPath(pathInfos.rootPath, openedDocUri) || ''
relativePath
);
}
if (pathInfos.isNotOkay) {
Expand All @@ -73,9 +91,11 @@ export class AppModel {
this.showPopUpMsg(`Server is Started at port : ${this.runningPort}`);

if (!Config.getNoBrowser) {
const relativePath = Helper.getSubPath(pathInfos.rootPath, openedDocUri) || '';
this.goLiveEvent.fire({ runningPort: this.runningPort, pathUri: relativePath });
this.openBrowser(
this.runningPort,
Helper.getSubPath(pathInfos.rootPath, openedDocUri) || ''
relativePath
);
}
}
Expand All @@ -99,6 +119,7 @@ export class AppModel {
this.showPopUpMsg(`Server is not already running`);
return;
}
this.goOfflineEvent.fire({ runningPort: this.runningPort });
LiveServerHelper.StopServer(this.LiveServerInstance, () => {
this.showPopUpMsg('Server is now offline.');
this.ToggleStatusBar();
Expand Down