Skip to content
This repository has been archived by the owner on Apr 1, 2020. It is now read-only.

Commit

Permalink
[WIP] Fix #384 - Python Language Server support (#411)
Browse files Browse the repository at this point in the history
* Add index.js

* Add stderror handling

* Prototype full document sync for Python

* Some cleanup

* Start cleaning up python language client

* Remove arguments for now

* Revert 'file://' change made for golang server, because that doesn't work on Windows anyway.

* Update PLAN.md

* Update PLAN.md

* Sys example

* Clean up python a bit

* Add typings for text document sync, add support for branching sync strategy

* Fix error in full textdocumentsync strategy

* Remove extra debugger statement

* Add logging for language client

* Some quickInfo improvements to support Python

* Update PLAN.md

* Fix lint issues

* Remove PLAN.md

* Add Python support documentation to README

* Fix file prefix across platforms

* Add platform-specific behavior for file:// URL handling
  • Loading branch information
extr0py authored Jun 3, 2017
1 parent 5fe6de0 commit 114ac92
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 24 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- [JavaScript and TypeScript](#javascript-and-typescript)
- [C#](#c)
- [Go](#go)
- [Python](#python)
- [Configuration](#configuration)
- [Extensibility](#extensibility)
- [FAQ](#faq)
Expand Down Expand Up @@ -263,6 +264,24 @@ _Known Issues_

- There is no Windows support at the moment - this is being tracked by [sourcegraph/go-langserver#113](https://github.com/sourcegraph/go-langserver/issues/113).

#### Python

_Configuration_

Python language support depends on [pyls](https://github.com/palantir/python-language-server) by [Palantir](https://www.palantir.com/), which provides language support for Python. Follow their installation instructions as this language server is not bundled out-of-the-box with Oni.

> `pyls` must be available in your PATH
_Supported Language features_

| Completion | Goto Definition | Formatting | Enhanced Syntax Highlighting | Quick Info | Signature Help | Live Evaluation | Debugging |
| --- | --- | --- | --- | --- | --- |--- | --- |
| Y | Y | N | N | Y | N | N | N |

_Known Issues_

- Windows support is blocked by this issue: [palantir/python-language-server#53](https://github.com/palantir/python-language-server/issues/53).

### Configuration

> ONI is configurable via a 'config.js' located in $HOME/.oni
Expand Down
38 changes: 30 additions & 8 deletions browser/src/Plugins/Api/LanguageClient/LanguageClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md
*/

import * as os from "os"

import * as _ from "lodash"
import * as rpc from "vscode-jsonrpc"
import * as types from "vscode-languageserver-types"
Expand Down Expand Up @@ -68,6 +70,7 @@ export class LanguageClient {
private _currentOpenDocumentPath: string
private _currentBuffer: string[] = []
private _initializationParams: LanguageClientInitializationParams
private _serverCapabilities: Helpers.ServerCapabilities

constructor(
private _startOptions: ServerRunOptions,
Expand Down Expand Up @@ -142,6 +145,7 @@ export class LanguageClient {
new LanguageClientLogger())

this._currentOpenDocumentPath = null
this._serverCapabilities = null

this._connection.onNotification(Helpers.ProtocolConstants.Window.LogMessage, (args) => {
console.log(JSON.stringify(args)) // tslint:disable-line no-console
Expand Down Expand Up @@ -174,6 +178,12 @@ export class LanguageClient {
this._connection.listen()

return this._connection.sendRequest(Helpers.ProtocolConstants.Initialize, initializationParams)
.then((response: any) => {
console.log(`[LANGUAGE CLIENT: ${initializationParams.clientName}]: Initialized`) // tslint:disable-line no-console
if (response && response.capabilities) {
this._serverCapabilities = response.capabilities
}
}, (err) => console.error(err))
}

public end(): Promise<void> {
Expand Down Expand Up @@ -247,8 +257,8 @@ export class LanguageClient {

if (contents.length === 0) {
return null
} else if (contents.length === 1) {
const title = contents[0]
} else if (contents.length === 1 && contents[0]) {
const title = contents[0].trim()

if (!title) {
return null
Expand All @@ -259,12 +269,16 @@ export class LanguageClient {
description: "",
}
} else {

const description = [...contents]
description.shift()
const descriptionContent = description.join(os.EOL)

return {
title: contents[0],
description: contents[1],
description: descriptionContent,
}
}

})
}

Expand Down Expand Up @@ -299,11 +313,19 @@ export class LanguageClient {
const lineNumber = args.lineNumber

const previousLine = this._currentBuffer[lineNumber - 1]

this._currentBuffer[lineNumber - 1] = changedLine

this._connection.sendNotification(Helpers.ProtocolConstants.TextDocument.DidChange,
Helpers.incrementalBufferUpdateToDidChangeTextDocumentParams(args, previousLine))
if (this._serverCapabilities && this._serverCapabilities.textDocumentSync) {
let changeTextDocumentParams

if (this._serverCapabilities.textDocumentSync === Helpers.TextDocumentSyncKind.Full) {
changeTextDocumentParams = Helpers.createDidChangeTextDocumentParams(args.eventContext.bufferFullPath, this._currentBuffer, args.eventContext.version)
} else {
changeTextDocumentParams = Helpers.incrementalBufferUpdateToDidChangeTextDocumentParams(args, previousLine)
}

this._connection.sendNotification(Helpers.ProtocolConstants.TextDocument.DidChange, changeTextDocumentParams)
}

return Promise.resolve(null)
}
Expand All @@ -321,7 +343,7 @@ export class LanguageClient {
})
} else {
this._connection.sendNotification(Helpers.ProtocolConstants.TextDocument.DidChange,
Helpers.bufferUpdateToDidChangeTextDocumentParams(args))
Helpers.createDidChangeTextDocumentParams(bufferFullPath, lines, args.eventContext.version))
}

return Promise.resolve(null)
Expand Down
47 changes: 32 additions & 15 deletions browser/src/Plugins/Api/LanguageClient/LanguageClientHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import * as os from "os"

import * as _ from "lodash"
import * as types from "vscode-languageserver-types"

export const ProtocolConstants = {
Expand All @@ -25,16 +26,27 @@ export const ProtocolConstants = {
},
}

export namespace TextDocumentSyncKind {
export const None = 0
export const Full = 1
export const Incremental = 2
}

// ServerCapabilities
// Defined in the LSP protocol: https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md
export interface ServerCapabilities {
textDocumentSync?: number
}

export const wrapPathInFileUri = (path: string) => getFilePrefix() + path

export const unwrapFileUriPath = (uri: string) => decodeURIComponent((uri).split(getFilePrefix())[1])

export const getTextFromContents = (contents: types.MarkedString | types.MarkedString[]): string[] => {
if (contents instanceof Array) {
return contents
.map((markedString) => getTextFromMarkedString(markedString))
return _.flatMap(contents, (markedString) => getTextFromMarkedString(markedString))
} else {
return [getTextFromMarkedString(contents)]
return getTextFromMarkedString(contents)
}
}

Expand All @@ -61,9 +73,7 @@ export const eventContextToTextDocumentPositionParams = (args: Oni.EventContext)
},
})

export const bufferUpdateToDidChangeTextDocumentParams = (args: Oni.BufferUpdateContext) => {
const lines = args.bufferLines
const { bufferFullPath, version } = args.eventContext
export const createDidChangeTextDocumentParams = (bufferFullPath: string, lines: string[], version: number) => {
const text = lines.join(os.EOL)

return {
Expand Down Expand Up @@ -94,19 +104,26 @@ export const incrementalBufferUpdateToDidChangeTextDocumentParams = (args: Oni.I
}
}

const getTextFromMarkedString = (markedString: types.MarkedString): string => {
const getTextFromMarkedString = (markedString: types.MarkedString): string[] => {
if (typeof markedString === "string") {
return markedString.trim()
return splitByNewlines(markedString)
} else {
// TODO: Properly apply syntax highlighting based on the `language` parameter
return markedString.value.trim()
return splitByNewlines(markedString.value)
}
}

const getFilePrefix = () => {
if (process.platform === "win32") {
return "file://"
} else {
return "file:///"
}
const splitByNewlines = (str: string) => {
// Remove '/r'
return str.split("\r")
.join("")
.split("\n")
}

const getFilePrefix = () => {
if (process.platform === "win32") {
return "file:///"
} else {
return "file://"
}
}
8 changes: 7 additions & 1 deletion browser/src/UI/components/QuickInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as os from "os"

import * as React from "react"
import { connect } from "react-redux"

Expand Down Expand Up @@ -72,7 +74,11 @@ export class QuickInfoTitle extends TextComponent {

export class QuickInfoDocumentation extends TextComponent {
public render(): JSX.Element {
return <div className="documentation">{this.props.text}</div>

const lines = this.props.text.split(os.EOL)
const divs = lines.map((l) => <div>{l}</div>)

return <div className="documentation">{divs}</div>
}
}

Expand Down
26 changes: 26 additions & 0 deletions vim/core/oni-plugin-python/lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const fs = require("fs")
const path = require("path")
const childProcess = require("child_process")

const activate = (Oni) => {

const serverOptions = {
command: "pyls",
}

const getInitializationOptionsAsync = (filePath) => {
return Promise.resolve({
clientName: "python",
rootPath: "file:///" + filePath,
capabilities: {
highlightProvider: true
}
})
}

const client = Oni.createLanguageClient(serverOptions, getInitializationOptionsAsync)
}

module.exports = {
activate
}
31 changes: 31 additions & 0 deletions vim/core/oni-plugin-python/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "oni-plugin-python",
"version": "0.0.1",
"main": "lib/index.js",
"engines": {
"oni": "^0.2.2"
},
"oni": {
"supportedFileTypes": [
"python"
],
"subscriptions": [
"buffer-update",
"vim-events"
],
"languageService": [
"quick-info",
"goto-definition",
"completion-provider",
"find-all-references",
"formatting",
"evaluate-block",
"signature-help"
],
"diagnosticsService": []
},
"dependencies": {
},
"devDependencies": {
}
}

0 comments on commit 114ac92

Please sign in to comment.