Skip to content

Commit

Permalink
taking shape
Browse files Browse the repository at this point in the history
  • Loading branch information
Yatao Li committed Sep 16, 2019
1 parent eeffb42 commit 7f829a6
Show file tree
Hide file tree
Showing 8 changed files with 619 additions and 90 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
node_modules
out
publish
coc-utils-*.tgz
3 changes: 3 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./repl"
export * from "./platform"
export * from "./utils"
470 changes: 469 additions & 1 deletion package-lock.json

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "coc-utils",
"version": "0.0.1",
"version": "0.0.5",
"description": "Utilities for coc.nvim extensions",
"main": "out/index.js",
"scripts": {
Expand All @@ -17,12 +17,15 @@
},
"devDependencies": {
"@types/node": "^10.3.3",
"typescript": "^3.0.3",
"coc.nvim": ">=0.0.71"
"coc.nvim": ">=0.0.71",
"typescript": "^3.0.3"
},
"engines": {
"coc": ">=0.0.71"
},
"files": [
"out"
],
"repository": {
"type": "git",
"url": "git+https://github.com/coc-extensions/coc-utils.git"
Expand Down
105 changes: 56 additions & 49 deletions platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/

import { workspace, extensions, ExtensionContext } from 'coc.nvim'
import {sleep} from "./utils"
import {workspace, extensions, ExtensionContext} from 'coc.nvim'
import {httpsGet, httpsGetJson} from "./utils"
import fs = require("fs");
import path = require("path");
import process = require("process");
import { IncomingMessage, RequestOptions, Agent } from 'http'
import { parse } from 'url'
import {IncomingMessage, RequestOptions, Agent, get} from 'http'
import {parse} from 'url'
const tunnel = require('tunnel')
const followRedirects = require("follow-redirects")
const unzip = require("extract-zip");
const rimraf = require("rimraf")

Expand Down Expand Up @@ -47,104 +46,112 @@ export function getPlatformDetails(): IPlatformDetails {
};
}

export function getPlatformSignature(): string
{
export function getPlatformSignature(): string {
const plat = getPlatformDetails()

const os_sig = (()=>{
switch(plat.operatingSystem){
const os_sig = (() => {
switch (plat.operatingSystem) {
case OperatingSystem.Windows: return "win"
case OperatingSystem.Linux: return "linux"
case OperatingSystem.MacOS: return "osx"
default: return "unknown"
}
})()

const arch_sig = (()=>{
if(plat.isProcess64Bit) return "x64"
const arch_sig = (() => {
if (plat.isProcess64Bit) return "x64"
else return "x86"
})()

return `${os_sig}-${arch_sig}`
}

export interface ILanguageServerPackage
{
export interface ILanguageServerPackage {
// the executable of the language server,
// in the downloaded and extracted package
executable: string
downloadUrl: string
platformPath: string
}

export interface ILanguageServerRepository
{
[platform:string]: ILanguageServerPackage
export interface ILanguageServerPackages {
[platform: string]: ILanguageServerPackage
}

export type LanguageServerDownloadChannel =
| { type: "nightly" }
| { type: "latest" }
| { type: "specific-tag" }
export type LanguageServerRepository =
| {kind: "github", repo: string, channel: string}
| {kind: "url-prefix", url: string}

export class LanguageServerProvider
{
interface IGithubAsset {
name: string
browser_download_url: string
}

interface IGithubRelease {
assets: IGithubAsset[]
}

export class LanguageServerProvider {
private extensionStoragePath: string
private languageServerName: string
private languageServerDirectory: string
private languageServerZip: string
private languageServerExe: string
private languageServerPackage: ILanguageServerPackage

constructor(private extension: ExtensionContext, private repo: ILanguageServerRepository, private channel: LanguageServerDownloadChannel)
{
constructor(extension: ExtensionContext, name: string, packs: ILanguageServerPackages, private repo: LanguageServerRepository) {
const platsig = getPlatformSignature()
this.languageServerName = name
this.extensionStoragePath = extension.storagePath
this.languageServerPackage = repo[platsig]
this.languageServerPackage = packs[platsig]

if(!this.languageServerPackage) { throw "Platform not supported" }
if (!this.languageServerPackage) {throw "Platform not supported"}

this.languageServerDirectory = path.join(this.extensionStoragePath, "server")
this.languageServerZip = this.languageServerDirectory + ".zip"
this.languageServerExe = path.join(this.languageServerDirectory, this.languageServerPackage.executable)
}

async getDownloadUrl(platfile: string): Promise<string> {
if (this.repo.kind === "github") {
let {repo: repo, channel: channel} = this.repo
let api_url = `https://api.github.com/repos/${repo}/releases/${channel}`
let api_result = await httpsGetJson<IGithubRelease>(api_url)
let matched_assets = api_result.assets.filter(x => x.name === platfile)
return matched_assets[0].browser_download_url
} else if (this.repo.kind === "url-prefix") {
return `${this.repo.url}/${platfile}`
}
throw new Error("unsupported repo kind.")
}

public async downloadLanguageServer(): Promise<void> {

let item = workspace.createStatusBarItem(0, {progress: true})
item.text = "Downloading F# Language Server"
item.text = `Downloading ${this.languageServerName}`
item.show()

if(!fs.existsSync(this.extensionStoragePath)) {
if (!fs.existsSync(this.extensionStoragePath)) {
fs.mkdirSync(this.extensionStoragePath)
}

if(fs.existsSync(this.languageServerDirectory)){
if (fs.existsSync(this.languageServerDirectory)) {
rimraf.sync(this.languageServerDirectory)
}

let url = this.languageServerPackage.downloadUrl

if(this.channel.type === "nightly")
{
url = url.replace("RELEASE", "nightly")
}
let platfile = this.languageServerPackage.platformPath
let url = await this.getDownloadUrl(platfile)

fs.mkdirSync(this.languageServerDirectory)

await new Promise<void>((resolve, reject) => {
const req = followRedirects.https.request(url, (res: IncomingMessage) => {
if (res.statusCode != 200) {
reject(new Error(`Invalid response from ${url}: ${res.statusCode}`))
return
}
let file = fs.createWriteStream(this.languageServerZip)
let stream = res.pipe(file)
stream.on('finish', resolve)
})
req.on('error', reject)
req.end()
await httpsGet(url, (resolve, _, res) => {
let file = fs.createWriteStream(this.languageServerZip)
let stream = res.pipe(file)
stream.on('finish', resolve)
})

await new Promise<void>((resolve, reject) => {
unzip(this.languageServerZip, {dir: this.languageServerDirectory}, (err: any) => {
if(err) reject(err)
if (err) reject(err)
else resolve()
})
})
Expand Down
73 changes: 45 additions & 28 deletions repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,40 +79,57 @@ export class REPLProcess {
}
}

let currentREPL: REPLProcess = undefined
async function createREPL () {
if(currentREPL) {
currentREPL.dispose()
currentREPL = undefined
}
currentREPL = new REPLProcess("F# REPL", "dotnet", ["fsi", "--readline+"])
currentREPL.onExited(() => {
currentREPL = undefined
})
await currentREPL.start()
return currentREPL.onExited
export interface IREPLDescriptor
{
filetype: string
title: string
command: string
args: string[]
// some REPLs require a special sequence to be sent
// to commit evaluation.
commit: string
}

export class REPLProvider {
private m_proc: REPLProcess = undefined

export async function doEval(mode: string) {

let document = await workspace.document
if (!document || document.filetype !== 'fsharp') {
return
constructor(public desc: IREPLDescriptor) {
}

if(!currentREPL) {
await createREPL()
async createREPL() {
if (this.m_proc) {
this.m_proc.dispose()
this.m_proc = undefined
}
this.m_proc = new REPLProcess(this.desc.title, this.desc.command, this.desc.args)
this.m_proc.onExited(() => {
this.m_proc = undefined
})
await this.m_proc.start()
return this.m_proc.onExited
}

// TODO: move to workspace.getCurrentSelection when we get an answer:
// https://github.com/neoclide/coc.nvim/issues/933
const content = await getCurrentSelection(mode)
for(let line of content){
await currentREPL.eval(line)
public async eval(mode: string) {

let document = await workspace.document
if (!document || document.filetype !== this.desc.filetype) {
return
}

if (!this.m_proc) {
await this.createREPL()
}

// TODO: move to workspace.getCurrentSelection when we get an answer:
// https://github.com/neoclide/coc.nvim/issues/933
const content = await getCurrentSelection(mode)
for (let line of content) {
await this.m_proc.eval(line)
}
await this.m_proc.eval(this.desc.commit)
// see :help feedkeys
await workspace.nvim.call('eval', `feedkeys("\\<esc>${content.length}j", "in")`)
// await currentREPL.scrollToBottom()
}
await currentREPL.eval(";;")
// see :help feedkeys
await workspace.nvim.call('eval', `feedkeys("\\<esc>${content.length}j", "in")`)
// await currentREPL.scrollToBottom()
}

3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"lib": [ "es2016" ],
"sourceMap": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true
"esModuleInterop": true,
"declaration": true
},
"exclude": [
"node_modules"
Expand Down
44 changes: 36 additions & 8 deletions utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
import fs = require("fs");
import os = require("os");
import path = require("path");
import { workspace, ExtensionContext, commands, TerminalResult } from 'coc.nvim';
import { Uri } from 'coc.nvim'
import { Range } from 'vscode-languageserver-protocol';
import {REPLProcess} from './repl';
import {workspace} from 'coc.nvim';
import {Uri} from 'coc.nvim'
import {IncomingMessage} from 'http';

const followRedirects = require("follow-redirects")

export function fileURLToPath(x: string) {
return Uri.parse(x).fsPath
Expand Down Expand Up @@ -64,11 +65,10 @@ export async function getCurrentSelection(mode: string) {
let doc = await workspace.document

if (mode === "v" || mode === "V") {
let [from, _ ] = await doc.buffer.mark("<")
let [to, __ ] = await doc.buffer.mark(">")
let [from,] = await doc.buffer.mark("<")
let [to,] = await doc.buffer.mark(">")
let result: string[] = []
for(let i = from; i <= to; ++i)
{
for (let i = from; i <= to; ++i) {
result.push(doc.getline(i - 1))
}
return result
Expand All @@ -87,3 +87,31 @@ export async function getCurrentSelection(mode: string) {
return []
}

export function httpsGet<T>(
url: string,
cb: (resolve: (value?: T | PromiseLike<T>) => void,
reject: (reason?: any) => void,
res: IncomingMessage)
=> void) {
return new Promise<T>((resolve, reject) => {
const req = followRedirects.https.request(url, (res: IncomingMessage) => {
if (res.statusCode != 200) {
reject(new Error(`Invalid response from ${url}: ${res.statusCode}`))
return
}
cb(resolve, reject, res)
})
req.on('error', reject)
req.end()
})
}

export function httpsGetJson<T>(url: string): Promise<T> {
return httpsGet(url, (resolve, reject, response) => {
let data = ''
response.on('data', chunk => data += chunk)
response.on('end', () => {
resolve(JSON.parse(data))
})
})
}

0 comments on commit 7f829a6

Please sign in to comment.