langd

package module
v0.0.0-...-d0cf9b8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2018 License: MIT Imports: 28 Imported by: 0

README

langd

Notes

Server behavior

When scanning the root URI for folders with Go code, we are skipping:

  • any directory that begins with "." (i.e., .git, .vscode).
  • any directory that is symlinked. See filepath.Walk description.
gRPC and Proto

If the gRPC API is changed (specifically, /proto/langd.proto), use the following command to rebuild the generated files.

protoc -I proto proto/langd.proto --go_out=plugins=grpc:proto

Initialization

Noting the LSP spec for initialization, initialize should be the first request on a connection. In this implementation, this request will be responded to before workspace initialization is complete. The initialize responce will announce what capabilities the server has, which should include the openClose option. If this workspace has been opened before, some files may automatically open, and the server may receive a didOpen request before initialiation is completed.

Connection & handler initialization has three stages: uninitialized, initializing, and initialized. Expectations for handling requests are detailed in the Initialize Request documentation in the LSP spec.

In the uninitialized state...

* for a request the respond should be errored with code: -32002. The message can be picked by the server.
* notifications should be dropped, except for the exit notification. This will allow the exit a server without an initialize request.

After the client has sent an initialize request, the client is expected to not send any further requests until it receives the InitializeResult response.

The server may return an InitializeResult response before it is ready to process requests. This is the initializing state. During this time, the client may send requests, and the server must queue them up for processing. The queue is not being processed at this time.

Once internal initialization is complete, the server is in the initialized stage, and will begin processing the queue. New requests are still queued up, but the server is free to process them.

Loading the workspace

The initialize request points to a filesystem path, which will be the root of the workspace. That path and every directory under it will be scanned for Go code. Additionally, and imported packages are loaded, down to the standard library.

During the loading process, the loader engine keeps a map of discovered distinct packages; a distinct package is a set of package files differentiated by OS, architecture, or other build flags. Each time that a distinct package is encountered, the map be checked, and if the entry is missing, a goroutine will be started to load the AST.

As each package is loaded (and all dependencies are loaded), that package is fed to config.Check to build the map of uses, definitions, etc.

Processing requests

Incoming requests are asynchronously processed by a connection handler. A connection handler has two queues: incomingQueue and outgoingQueue. As requests are received from the JSONRPC2 server, they are handed off to the connection handler, which looks up and instantiates a request handler by method name. The request handler immediately performs some preprocessing on the request to unmarshal arguments and perform any other setup. Once the preprocessing is complete, the request handler is placed on the incomingQueue.

The incomingQueue and outgoingQueue are processed in a GoRoutine. When a request handler is pulled off the incomingQueue, the work method is invoked, which is expected to perform the processing of the actual request. Once this is complete, the request handler is checked to see if it is also a reply handler, and if so, the request / reply handler is placed on the outgoingQueue. Notifications do not reply, so those requests are not placed on the outgoingQueue.

Replies are supposed to be sent in same order as the requests. However, if the requests are processed asynchronously and some are faster to complete than others, then there is potenial for out-of-order replies. Additionally, some requests may require some inherent synchronous processing. For example, if the client sends a sequence of didChange notifications, those will need to be processed in order, and before a definition request is processed (as the didChange may have some bearing on the request of a definition). For this reason, we use the Sigqueue struct to control the order of operations.

TODO: Describe Sigqueue in greater detail, as it applies here

The connection handler may need a RWMutex to handle requests. Requests which do not alter state (textDocument/definition, textDocument/references, etc) enter with a Read lock, allowing any other non-altering requests to enter as well. Once a request which would alter state is processed (textDocument/didChange, textDocument/rename, etc), a Write lock is requested. All currently running read operations will need to complete before the write can proceed, and each write operation will need to proceed synchronously. (Conceivably, some write operations could be performed asynchronously, but that is out of scope for an initial implementation.)

Because replies may be generated out of order with asynchronous processing, they must be queued up in the outgoingQueue.

Changes to files

When the IDE opens a file for the user, a textDocument/didOpen request is sent to the server. The server creates a rope representation of the file contents, which is altered with textDocument/didChange requests.

Extensions to the Language Server Protocol

Server Health request

The client may request some basic health metrics from the server; including instanteous CPU and memory usage.

Request

  • method: 'health/instant'
  • params: none

Response

  • result: Instant defined as follows:
interface HealthInformation {
	/**
	 *
	 */
	cpu: number;

	/**
	 *
	 */
	memory: number;
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CalculateOffsetForPosition

func CalculateOffsetForPosition(read io.Reader, line, character int) (int, error)

CalculateOffsetForPosition scans a rope to get to the rune offset at the given line and character

func WithinPosition

func WithinPosition(target, start, end *token.Position) bool

WithinPosition checks to see if a target position is inclusively within two other positions.

Types

type DistinctPackage

type DistinctPackage struct {
	Package *Package
	// contains filtered or unexported fields
}

DistinctPackage contains the os/arch specific package AST

func NewDistinctPackage

func NewDistinctPackage(l *Loader, p *Package) *DistinctPackage

NewDistinctPackage returns a new instance of DistinctPackage

func (*DistinctPackage) Hash

func (dp *DistinctPackage) Hash() collections.Hash

Hash returns the hash for this distinct package

func (*DistinctPackage) Invalidate

func (dp *DistinctPackage) Invalidate()

Invalidate sets the checker to nil and the loadState to unloaded

func (*DistinctPackage) String

func (dp *DistinctPackage) String() string

func (*DistinctPackage) WaitUntilReady

func (dp *DistinctPackage) WaitUntilReady(loadState loadState)

WaitUntilReady blocks until this distinct package has loaded sufficiently for the requested load state.

type File

type File struct {
	// contains filtered or unexported fields
}

File is an AST file and any errors that types.Config.Check discovers

type FileError

type FileError struct {
	token.Position
	Message string
	Warning bool
}

FileError is a translation of the types.Error struct

type Loader

type Loader struct {
	StartDir string

	Tags []string

	Log *log.Log
	// contains filtered or unexported fields
}

Loader is the workspace-specific configuration and context for building and type-checking

func NewLoader

func NewLoader(le *LoaderEngine, goos, goarch, goroot string, options ...LoaderOption) *Loader

NewLoader creates a new Loader

func (*Loader) Errors

func (l *Loader) Errors(handleErrs func(file string, errs []FileError))

Errors exposes problems with code found during compilation on a file-by-file basis.

func (*Loader) FindDistinctPackage

func (l *Loader) FindDistinctPackage(absPath string) (*DistinctPackage, error)

FindDistinctPackage will locate the distinct package at the provided path

func (*Loader) FindImportPath

func (l *Loader) FindImportPath(dp *DistinctPackage, importPath string) (string, error)

func (*Loader) GetTags

func (l *Loader) GetTags() string

GetTags produces a string detailing the GOROOT, GOARCH, GOOS, and any tags

func (*Loader) HandleTypeCheckerError

func (l *Loader) HandleTypeCheckerError(e error)

HandleTypeCheckerError is invoked from the types.Checker when it encounters errors

func (*Loader) LoadDirectory

func (l *Loader) LoadDirectory(startDir string) error

LoadDirectory adds the contents of a directory to the Loader

func (*Loader) Signal

func (l *Loader) Signal()

func (*Loader) String

func (l *Loader) String() string

String is the implementation of fmt.Stringer

func (*Loader) Wait

func (l *Loader) Wait()

Wait blocks until all packages have been loaded

type LoaderEngine

type LoaderEngine struct {
	Log *log.Log
	// contains filtered or unexported fields
}

LoaderEngine is a Go code loader

func NewLoaderEngine

func NewLoaderEngine() *LoaderEngine

NewLoaderEngine creates a new loader

func (*LoaderEngine) Close

func (le *LoaderEngine) Close() error

Close stops the loader engine processing

func (*LoaderEngine) InvalidatePackage

func (le *LoaderEngine) InvalidatePackage(absPath string)

InvalidatePackage invalidates all the distinct packages that point to the package at this path, as well as any paths that import this, and down the import graph.

type LoaderOption

type LoaderOption func(l *Loader)

LoaderOption provides a hook for NewLoader to set or modify the new loader's build.Context

type OpenedFiles

type OpenedFiles struct {
	// contains filtered or unexported fields
}

OpenedFiles is a collection of files opened across the caravan

func NewOpenedFiles

func NewOpenedFiles() *OpenedFiles

NewOpenedFiles creates a new OpenedFiles instance

func (*OpenedFiles) Close

func (of *OpenedFiles) Close(absFilepath string) error

Close will remove a rope from the collection

func (*OpenedFiles) EnsureOpened

func (of *OpenedFiles) EnsureOpened(absFilepath, text string) error

EnsureOpened will create a new rope for a file that's not previously opened

func (*OpenedFiles) Get

func (of *OpenedFiles) Get(absFilepath string) (*rope.Rope, error)

Get returns the rope for a file

func (*OpenedFiles) Replace

func (of *OpenedFiles) Replace(absFilepath, text string) error

Replace will replace an existing rope with a completely new rope based on the provided text

type OverrunError

type OverrunError struct {
	// contains filtered or unexported fields
}

OverrunError is returned from CalculateOffsetForPosistion when the requested line does not have enough characters to find an offset

func NewOverrunError

func NewOverrunError(overrun OverrunType, line, char int) *OverrunError

NewOverrunError creates a new OverrunError instance

func (*OverrunError) Error

func (oe *OverrunError) Error() string

type OverrunType

type OverrunType int

OverrunType describes the reason for the overrun error

const (
	// Line indicates that the overrun occured because there were not enough
	// lines to get the offset for the requested position
	Line OverrunType = iota

	// Character indicates that the overrun occured because the line did not
	// have enough characters to get the offset for the requested position
	Character
)

type Package

type Package struct {
	AbsPath string

	Fset *token.FileSet
	// contains filtered or unexported fields
}

Package is the contents of a package

func NewPackage

func NewPackage(absPath string) *Package

NewPackage creates a new instance of a Package struct

func (*Package) Invalidate

func (p *Package) Invalidate()

Invalidate resets the checker state for all distinct packages

func (*Package) String

func (p *Package) String() string

type Workspace

type Workspace struct {
	LoaderEngine *LoaderEngine
	Loader       *Loader
	// contains filtered or unexported fields
}

Workspace is a mass of code

func CreateWorkspace

func CreateWorkspace(le *LoaderEngine, log *log.Log) *Workspace

CreateWorkspace returns a new instance of the Workspace struct

func (*Workspace) AssignLoader

func (w *Workspace) AssignLoader(l *Loader)

AssignLoader attaches the new loader context to the workspace. The workspace should start to reload the packages.

func (*Workspace) ChangeFile

func (w *Workspace) ChangeFile(absFilepath string, startLine, startCharacter, endLine, endCharacter int, text string) error

ChangeFile applies changes to an opened file

func (*Workspace) CloseFile

func (w *Workspace) CloseFile(absPath string) error

CloseFile will take a file out of the OpenedFiles struct and reparse

func (*Workspace) Hover

func (w *Workspace) Hover(p *token.Position) (string, error)

Hover supplies the hover text for a given position

func (*Workspace) LocateDeclaration

func (w *Workspace) LocateDeclaration(p *token.Position) (*token.Position, error)

LocateDeclaration returns the position where the provided identifier is declared & defined

func (*Workspace) LocateIdent

func (w *Workspace) LocateIdent(p *token.Position) (*ast.Ident, error)

LocateIdent scans the loaded fset for the identifier at the requested position

func (*Workspace) LocateReferences

func (w *Workspace) LocateReferences(p *token.Position) []token.Position

LocateReferences returns the array of positions where the given identifier is referenced or used

func (*Workspace) OpenFile

func (w *Workspace) OpenFile(absFilepath, text string) error

OpenFile shadows the file read from the disk with an in-memory version, which the workspace can accept edits to.

func (*Workspace) ReplaceFile

func (w *Workspace) ReplaceFile(absFilepath, text string) error

ReplaceFile replaces the entire contents of an opened file

Directories

Path Synopsis
Package proto is a generated protocol buffer package.
Package proto is a generated protocol buffer package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL