Skip to content

Commit

Permalink
Add documentation for the Clang.Completion module and fix several fla…
Browse files Browse the repository at this point in the history
…ws. #23
  • Loading branch information
sethfowler committed Jun 2, 2014
1 parent 81bea3c commit 8f8fc72
Show file tree
Hide file tree
Showing 8 changed files with 732 additions and 361 deletions.
1 change: 1 addition & 0 deletions LibClang.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Library
vector >= 0.10 && < 0.11
hs-source-dirs: src
c-sources: src/Clang/Internal/FFI_stub_ffi.c,
cbits/utils.c,
cbits/visitors.c
include-dirs: cbits
exposed-modules: Clang,
Expand Down
17 changes: 17 additions & 0 deletions cbits/utils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "utils.h"

unsigned codeCompleteGetNumResults(CXCodeCompleteResults* results)
{
return results ? results->NumResults
: 0;
}

// We don't perform any null or bounds checks here because this is an internal function
// with only one caller, but if we exposed this API publicly we'd have to do so.
enum CXCursorKind codeCompleteGetResult(CXCodeCompleteResults* results,
unsigned index,
CXCompletionString* stringOut)
{
*stringOut = results->Results[index].CompletionString;
return results->Results[index].CursorKind;
}
10 changes: 10 additions & 0 deletions cbits/utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <clang-c/Index.h>

// This file contains utility functions that make implementing the Haskell FFI
// code easier.

unsigned codeCompleteGetNumResults(CXCodeCompleteResults* results);

enum CXCursorKind codeCompleteGetResult(CXCodeCompleteResults* results,
unsigned index,
CXCompletionString* stringOut);
6 changes: 0 additions & 6 deletions src/Clang.hs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,7 @@ module Clang (
, FFI.CommentKind (..)
, FFI.CommentInlineCommandRenderKind (..)
, FFI.CommentParamPassDirection (..)
, FFI.CompletionString
, FFI.CompletionResult
, FFI.CompletionChunkKind(..)
, FFI.CodeCompleteFlags(..)
, FFI.CodeCompleteResults
, FFI.AvailabilityKind(..)
, FFI.CompletionContext(..)
, FFI.CursorKind(..)
, FFI.LinkageKind(..)
, FFI.LanguageKind(..)
Expand Down
245 changes: 185 additions & 60 deletions src/Clang/Completion.hs
Original file line number Diff line number Diff line change
@@ -1,100 +1,225 @@
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}

-- | Functions for performing code completion.
--
-- To get started with code completion, it's enough to parse a file with
-- 'Clang.parseSourceFile' and pass the 'FFI.TranslationUnit' to 'codeCompleteAt'.
-- This will return a 'FFI.CodeCompleteResults' value, from which you can
-- retrieve a list of completion strings using 'getResults'. Each completion
-- string in turn consists of a series of chunks, which you can retrieve using
-- 'getChunks'.
--
-- This module is intended to be imported qualified.
module Clang.Completion
( getChunkKind
, getChunkText
, getChunkCompletionString
, getNumChunks
, getPriority
, getAvailability
, getAnnotations
, getParent
, getBriefComment
, getCursorCompletionString
, defaultCodeCompleteOptions
, codeCompleteAt
(
-- * Code completion
codeCompleteAt
, FFI.CodeCompleteFlags(..)
, FFI.CodeCompleteResults

-- * Completion results
, getResults
, FFI.CompletionString
, sortResults
, getDiagnostics
, getContexts
, FFI.CompletionContext(..)
, getContainerKind
, getContainerUSR
, getObjCSelector

-- * Completion strings
, getChunks
, Chunk(..)
, FFI.ChunkKind(..)
, getPriority
, getAvailability
, getAnnotations
, getParent
, getBriefComment
) where

import Control.Applicative
import Control.Monad
import Control.Monad.IO.Class
import Data.Typeable
import qualified Data.Vector as DV

import Clang.Internal.BitFlags
import qualified Clang.Internal.FFI as FFI
import Clang.Internal.Monad

getChunkKind :: ClangBase m => FFI.CompletionString s' -> Int
-> ClangT s m FFI.CompletionChunkKind
getChunkKind cs i = liftIO $ FFI.getCompletionChunkKind cs i
-- | Perform code completion at a given location in a translation unit.
--
-- This function performs code completion at a particular file, line, and
-- column within source code, providing results that suggest potential
-- code snippets based on the context of the completion. The basic model
-- for code completion is that Clang will parse a complete source file,
-- performing syntax checking up to the location where code completion has
-- been requested. At that point, a special code completion token is passed
-- to the parser, which recognizes this token and determines, based on the
-- current location in the C \/ Objective-C \/ C++ grammar and the state of
-- semantic analysis, what completions to provide. These completions are
-- returned via a 'FFI.CodeCompleteResults' value.
--
-- Code completion itself is meant to be triggered by the client when the
-- user types punctuation characters or whitespace, at which point the
-- code completion location will coincide with the cursor. For example, if \'p\'
-- is a pointer, code completion might be triggered after the \'-\' and then
-- after the \'>\' in \'p->\'. When the code completion location is after the \'>\',
-- the completion results will provide, e.g., the members of the struct that
-- \'p\' points to. The client is responsible for placing the cursor at the
-- beginning of the token currently being typed, then filtering the results
-- based on the contents of the token. For example, when code-completing for
-- the expression \'p->get\', the client should provide the location just after
-- the \'>\' (e.g., pointing at the \'g\') to this code completion hook. Then, the
-- client can filter the results based on the current token text (\'get\'), only
-- showing those results that start with \'get\'. The intent of this interface
-- is to separate the relatively high-latency acquisition of code completion
-- results from the filtering of results on a per-character basis, which must
-- have a lower latency.
codeCompleteAt ::
ClangBase m
=> FFI.TranslationUnit s' -- ^ The translation unit in which code completion should occur.
-- The source files for this translation unit need not be
-- completely up-to-date (and the contents of those source files
-- may be overridden via the 'FFI.UnsavedFile' vector). Cursors
-- referring into the translation unit may be invalidated by
-- this invocation.
-> FilePath -- ^ The name of the source file where code
-- completion should be performed. This filename may be any file
-- included in the translation unit.
-> Int -- ^ The line at which code completion should occur.
-> Int -- ^ The column at which code completion should occur.
-- Note that the column should point just after the syntactic construct that
-- initiated code completion, and not in the middle of a lexical token.
-> DV.Vector FFI.UnsavedFile -- ^ Files that have not yet been saved to disk
-- but may be required for parsing or code completion, including
-- the contents of those files. The contents and name of these
-- files (as specified by 'FFI.UnsavedFile') are copied when
-- necessary, so the client only needs to guarantee their
-- validity until the call to this function returns.
-> Maybe [FFI.CodeCompleteFlags] -- ^ Extra options that control the behavior of code
-- completion, or 'Nothing' to use the default set.
-> ClangT s m (FFI.CodeCompleteResults s)
codeCompleteAt t fname l c ufs mayOpts = do
opts <- case mayOpts of
Just os -> return os
Nothing -> unFlags <$> liftIO FFI.defaultCodeCompleteOptions
FFI.codeCompleteAt t fname l c ufs (orFlags opts)

-- | Retrieves a list of code completion results.
--
-- The first element of each tuple is the completion string, which describes how to insert
-- this result into the editing buffer. Use 'getChunks' to analyze it further.
--
-- The second element of each tuple is the kind of entity that this completion refers to.
-- It will be a macro, keyword, or declaration describing the entity that the completion
-- is referring to.
getResults :: ClangBase m => FFI.CodeCompleteResults s'
-> ClangT s m [(FFI.CompletionString s, FFI.CursorKind)]
getResults rs = do
numS <- liftIO $ FFI.codeCompleteGetNumResults rs
forM [0..(numS - 1)] $ \i ->
FFI.codeCompleteGetResult rs i

-- | Sort the provided code completion results in case-insensitive alphabetical order.
sortResults :: ClangBase m => FFI.CodeCompleteResults s' -> ClangT s m ()
sortResults c = liftIO $ FFI.sortCodeCompletionResults c

-- | Retrieves the diagnostics associated with the given code completion.
getDiagnostics :: ClangBase m => FFI.CodeCompleteResults s' -> ClangT s m [FFI.Diagnostic s]
getDiagnostics c = do
numD <- liftIO $ FFI.codeCompleteGetNumDiagnostics c
mapM (FFI.codeCompleteGetDiagnostic c) [0..(numD - 1)]

getChunkText :: ClangBase m => FFI.CompletionString s' -> Int -> ClangT s m (FFI.ClangString s)
getChunkText = FFI.getCompletionChunkText
-- | Determines which kinds of completions are appropriate for the context
-- of the given code completion.
getContexts :: ClangBase m => FFI.CodeCompleteResults s' -> ClangT s m [FFI.CompletionContext]
getContexts rs = unFlags <$> liftIO (FFI.codeCompleteGetContexts rs)

getChunkCompletionString :: ClangBase m => FFI.CompletionString s' -> Int ->
ClangT s m (FFI.CompletionString s)
getChunkCompletionString cs i = liftIO $ FFI.getCompletionChunkCompletionString cs i
-- | Returns a cursor kind for the container associated with the given code
-- completion. Only contexts like member accesses and Objective-C message sends
-- have containers; for other kinds of contexts, a cursor kind of
-- 'FFI.InvalidCodeCursor' is returned.
--
-- The second element of the result tuple is a boolean indicating whether libclang
-- has incomplete information about the container. A 'True' value indicates that
-- information about the container is incomplete.
getContainerKind :: ClangBase m => FFI.CodeCompleteResults s'
-> ClangT s m (FFI.CursorKind, Bool)
getContainerKind rs = liftIO $ FFI.codeCompleteGetContainerKind rs

getNumChunks :: ClangBase m => FFI.CompletionString s' -> ClangT s m Int
getNumChunks cs = liftIO $ FFI.getNumCompletionChunks cs
-- | Given a code completion context, returns a "Clang.USR" for the appropriate
-- container. Only contexts like member accesses and Objective-C message sends
-- have containers; for other kinds of contexts, the empty string is returned.
getContainerUSR :: ClangBase m => FFI.CodeCompleteResults s' -> ClangT s m (FFI.ClangString s)
getContainerUSR = FFI.codeCompleteGetContainerUSR

-- | Returns the currently-entered selector for an Objective-C message
-- send, formatted like \"initWithFoo:bar:\". This function is only guaranteed
-- to return a non-empty string if the completion context is an Objective-C
-- instance message or class message send, which you can check with 'getContexts'.
getObjCSelector :: ClangBase m => FFI.CodeCompleteResults s' -> ClangT s m (FFI.ClangString s)
getObjCSelector = FFI.codeCompleteGetObjCSelector

-- | The completion string is a template that describes not only the completion itself,
-- but also information about how it should be presented to the user. It is divided into
-- a list of chunks, with each kind of chunk playing a different role in the template.
data Chunk s
= TextChunk FFI.ChunkKind (FFI.ClangString s)
| OptionalChunk (FFI.CompletionString s)
deriving (Eq, Ord, Typeable)

-- | Retrieves the chunks within a completion string.
--
-- Each \"chunk\" contains either a piece of text with a specific \"kind\"
-- that describes how that text should be interpreted by the client, or
-- another completion string.
getChunks :: ClangBase m => FFI.CompletionString s' -> ClangT s m [Chunk s]
getChunks cs = do
numC <- liftIO $ FFI.getNumCompletionChunks cs
forM [0..(numC - 1)] $ \i -> do
kind <- liftIO $ FFI.getCompletionChunkKind cs i
case kind of
FFI.OptionalChunkKind ->
OptionalChunk <$> (liftIO $ FFI.getCompletionChunkCompletionString cs i)
_ ->
TextChunk kind <$> FFI.getCompletionChunkText cs i

-- | Determines the priority of this code completion string.
--
-- The priority of a code completion indicates how likely it is that this
-- particular completion is the completion that the user will select. The
-- priority is selected by various internal heuristics. Smaller values
-- indicate more likely completions.
getPriority :: ClangBase m => FFI.CompletionString s' -> ClangT s m Int
getPriority cs = liftIO $ FFI.getCompletionPriority cs

-- | Determines the availability of the entity that this code completion
-- string refers to.
getAvailability :: ClangBase m => FFI.CompletionString s' -> ClangT s m FFI.AvailabilityKind
getAvailability cs = liftIO $ FFI.getCompletionAvailability cs

-- | Retrieves the annotations associated with the given completion string.
getAnnotations :: ClangBase m => FFI.CompletionString s' -> ClangT s m [FFI.ClangString s]
getAnnotations cs = do
numA <- liftIO $ FFI.getCompletionNumAnnotations cs
mapM (FFI.getCompletionAnnotation cs) [0..(numA - 1)]

-- | Retrieves the parent context of the given completion string.
--
-- The parent context of a completion string is the semantic parent of
-- the declaration (if any) that the code completion represents. For example,
-- a code completion for an Objective-C method would have the method's class
-- or protocol as its context. A completion string representing a method on
-- the NSObject class might have a parent context of \"NSObject\".
getParent :: ClangBase m => FFI.CompletionString s' -> ClangT s m (FFI.ClangString s)
getParent = FFI.getCompletionParent

-- | Retrieves the brief documentation comment attached to the declaration
-- that corresponds to the given completion string.
getBriefComment :: ClangBase m => FFI.CompletionString s' -> ClangT s m (FFI.ClangString s)
getBriefComment = FFI.getCompletionBriefComment

getCursorCompletionString :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.CompletionString s)
getCursorCompletionString c = liftIO $ FFI.getCursorCompletionString c

defaultCodeCompleteOptions :: ClangBase m => ClangT s m [FFI.CodeCompleteFlags]
defaultCodeCompleteOptions = unFlags <$> liftIO FFI.defaultCodeCompleteOptions

codeCompleteAt ::
ClangBase m =>
FFI.TranslationUnit s'
-> FilePath -- ^ Filename of the source file
-> Int -- ^ Line in the source file
-> Int -- ^ Column on the line
-> DV.Vector FFI.UnsavedFile -- ^ Unsaved files so far
-> [FFI.CodeCompleteFlags]
-> ClangT s m (FFI.CodeCompleteResults s)
codeCompleteAt t fname l c ufs opts = FFI.codeCompleteAt t fname l c ufs (orFlags opts)

sortResults :: ClangBase m => FFI.CodeCompleteResults s' -> Int -> ClangT s m ()
sortResults c i = liftIO $ FFI.sortCodeCompletionResults c i

getDiagnostics :: ClangBase m => FFI.CodeCompleteResults s' -> ClangT s m [FFI.Diagnostic s]
getDiagnostics c = do
numD <- liftIO $ FFI.codeCompleteGetNumDiagnostics c
mapM (FFI.codeCompleteGetDiagnostic c) [0..(numD - 1)]

getContexts :: ClangBase m => FFI.CodeCompleteResults s' -> ClangT s m [FFI.CompletionContext]
getContexts rs = unFlags <$> liftIO (FFI.codeCompleteGetContexts rs)

getContainerKind :: ClangBase m => FFI.CodeCompleteResults s'
-> ClangT s m (FFI.CursorKind, Bool)
getContainerKind rs = liftIO $ FFI.codeCompleteGetContainerKind rs

getContainerUSR :: ClangBase m => FFI.CodeCompleteResults s' -> ClangT s m (FFI.ClangString s)
getContainerUSR = FFI.codeCompleteGetContainerUSR

getObjCSelector :: ClangBase m => FFI.CodeCompleteResults s' -> ClangT s m (FFI.ClangString s)
getObjCSelector = FFI.codeCompleteGetObjCSelector
9 changes: 9 additions & 0 deletions src/Clang/Cursor.hs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ module Clang.Cursor

-- Platform availability
, getCursorPlatformAvailability

, getCompletionString
) where

import Control.Applicative
Expand Down Expand Up @@ -315,3 +317,10 @@ getCursorKindSpelling = FFI.getCursorKindSpelling
getCursorPlatformAvailability :: ClangBase m => FFI.Cursor s'
-> ClangT s m (FFI.PlatformAvailabilityInfo s)
getCursorPlatformAvailability = FFI.getCursorPlatformAvailability

-- | Retrieve a completion string for an arbitrary declaration or macro
-- definition cursor.
--
-- Completion strings can be manipulated using the functions in "Clang.Completion".
getCompletionString :: ClangBase m => FFI.Cursor s' -> ClangT s m (FFI.CompletionString s)
getCompletionString = FFI.getCursorCompletionString
Loading

0 comments on commit 8f8fc72

Please sign in to comment.