Using the Dscanner (D Language) inside Zeus

Find Tips and tricks on how to better use the Zeus IDE. Feel free to post your own tips but please do not post bug reports, feature requests or questions here.
Post Reply
jussij
Site Admin
Posts: 2650
Joined: Fri Aug 13, 2004 5:10 pm

Using the Dscanner (D Language) inside Zeus

Post by jussij »

The Dscanner is a tool designed to analyzing D source code.

NOTE: Other Zeus, D language specific features can be found here:
Using the dfmt with Zeus
Using the D Programming Language with Zeus
Using the DCD with Zeus

These are just some of the features this tool has to offer:

Syntax Check

The "--syntaxCheck" or "-s" option prints a listing of any errors or warnings found while lexing or parsing the given source file.

Style Check

The "--styleCheck" or "-S" option runs some basic static analysis checks against the given source files.

Find Declaration

The "--declaration" or "-d" options allow you to search for a symbols declaration.

Example of Using Find Declaration
Assume this file is open:

Code: Select all

Dscanner\astprinter.d
In that file there are these lines of code:

Code: Select all

override void visit(const AddExpression addExpression)
{
    ...
}
With the cursor placed on the AddExpression word, running the Find Declaration tool results in this output:

Code: Select all

.\libdparse\src\std\d\ast.d(371:13)
Double clicking on that line of output loads the ast.d file and places the cursor on this line of code in that file:

Code: Select all

final class AddExpression : ExpressionNode
Zeus Configuration
To use the Dscanner from inside Zeus you will need to do the following:

1) Download the source code from the Dscanner home page.
2) Build the dscanner.exe and copy the executable to this folder (which will issure it is in the PATH):
3) Edit the D Document Type and in the Tools section add the three tool configurations described below.

Code: Select all

     Menu Test: Syntax Check
  Program Name: dscanner.exe
     Arguments: --syntaxCheck "$fn"
Work Directory: $fdd
    (o) Run Hidden
    [x] Capture Standard Output
    [x] Capture Standard Error

Code: Select all

     Menu Test: Style Check
  Program Name: dscanner.exe
     Arguments: --styleCheck "$fn"
Work Directory: $fdd
    (o) Run Hidden
    [x] Capture Standard Output
    [x] Capture Standard Error

Code: Select all

     Menu Test: Find Declaration
  Program Name: dscanner.exe
     Arguments: --declaration $wex
Work Directory: $fdd
    (o) Run Hidden
    [x] Capture Standard Output
    [x] Capture Standard Error
To use these tools just open a file containing D source code and select the tool from the Tools menu.

In the following post find details on how to wrap these calls to the Dscanner tool inside a Zeus macro.

By using a macro allows you to do things like configure Zeus to run these style checks each time the file is saved or to have these checks run using a keyboard key combination.
Last edited by jussij on Tue Sep 02, 2014 3:32 am, edited 1 time in total.
jussij
Site Admin
Posts: 2650
Joined: Fri Aug 13, 2004 5:10 pm

Post by jussij »

Below are Zeus Python scripts that wrap the Dscanner functionality inside a macro.

By running Dscanner from inside a macro it is easier to bind the functionality to the keyboard or to have the macro run on particular events like the file save event.

The four files below should be saved to the Zeus zScript folder.

dscanner_syntax_check.py

Code: Select all

#
#        Name: Dscanner Style Check
#
#      Author: Jussi Jumppanen
#
#    Language: Python
#
# Description: This Zeus macro uses the Dscanner utility to implement
#              style check feature.
#
#                  https://github.com/Hackerpilot/Dscanner
#
#              For this macro to work the Dscanner executable needs to be
#              in the Zeus install folder or in the PATH.
import zeus
import dscanner_code

def key_macro():
    if zeus.is_document() == 1:
        # save the current file
        result = zeus.FileSave()

        if (result == 1):
            # use the currently active document
            file_name = zeus.macro_tag("$fn")

            # do the style check
            dscanner_code.styleCheck(file_name)
        else:
            zeus.message("The operation cancelled by user.")
            zeus.beep()
            return

key_macro() # run the macro
dscanner_style_check.py

Code: Select all

#
#        Name: Dscanner Style Check
#
#      Author: Jussi Jumppanen
#
#    Language: Python
#
# Description: This Zeus macro uses the Dscanner utility to implement
#              style check feature.
#
#                  https://github.com/Hackerpilot/Dscanner
#
#              For this macro to work the Dscanner executable needs to be
#              in the Zeus install folder or in the PATH.
import zeus
import dscanner_code

def key_macro():
    if zeus.is_document() == 1:
        # save the current file
        result = zeus.FileSave()

        if (result == 1):
            # use the currently active document
            file_name = zeus.macro_tag("$fn")

            # do the style check
            dscanner_code.styleCheck(file_name)
        else:
            zeus.message("The operation cancelled by user.")
            zeus.beep()
            return

key_macro() # run the macro
dscanner_find_declaration.py file

Code: Select all

#
#        Name: Dscanner Find Declaration
#
#      Author: Jussi Jumppanen
#
#    Language: Python
#
# Description: This Zeus macro uses the Dscanner utility to implement
#              find declaration feature.
#
#                  https://github.com/Hackerpilot/Dscanner
#
#              For this macro to work the Dscanner executable needs to be
#              in the Zeus install folder or in the PATH.
import os
import zeus
import dscanner_code

def key_macro():
    symbol = zeus.macro_tag("$wex")

    if len(symbol) > 0:
        directory = zeus.macro_tag("$fdd")

        # change to the directory of the active file
        os.chdir(directory)

        # try to find the symbol
        dscanner_code.findDeclaration(symbol)
    else:
        zeus.message("No symbol found under the current cursor.")
        zeus.beep()

key_macro() # run the macro
The script file below holds common code that is used by the three previous scripts.

dscanner_code.py file

Code: Select all

#
#        Name: Dscanner Common Code
#
#      Author: Jussi Jumppanen
#
#    Language: Python
#
# Description: This macro file contains the common code needed to make Zeus
#              work with the Dscanner utility.
#
#                  https://github.com/Hackerpilot/Dscanner
#
#              For this macro to work the Dscanner executable needs to be
#              in the Zeus install folder or in the PATH.
#
import os
import re
import time
import zeus
import subprocess

def outputFileName(file_name):
    file_name_err = file_name + ".ztow"

    # check that the file constains no whitespace
    if (file_name.find(' ') <> -1):
        file_name = "\"" + file_name + "\""
        file_name_err = "\"" + file_name_err  + "\""

    return file_name, file_name_err

def runCommand(command, file_output):
    # remove any previous output file
    if os.path.exists(file_output):
        os.remove(file_output)

    #  1 - save the document before running the program
    #  2 - capture any standard output generated by the program
    #  4 - capture any standard error generated by the program
    #  8 - ask for additional arguments
    # 16 - the program will use the MS-DOS command interpreter (ie. dir *.* etc)
    # 32 - wait for the program to complete (the ESC key will cancel the wait)
    # 64 - run the program in a visible DOS session (otherwise runs hidden)
    flags = 1+16+32

    # run the command
    result = zeus.system(command, "", flags, "shit")

    #zeus.message_box(0, command)

    have_errors = 0

    # che3ck for any error output details
    if os.path.exists(file_output):
        if os.stat(file_output).st_size > 0:
            have_errors = 1

    return have_errors

def runCommandEx(command):
    # initialise the startup info to run hidden
    info = subprocess.STARTUPINFO()
    info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    info.wShowWindow = subprocess.SW_HIDE

    result = ""
    error  = ""

    # run the command and capture the output
    try:
        # run the command line
        p = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, startupinfo=info)

        # wait for the process to return
        while p.poll() is None:
            time.sleep(0.20)

        # get the process output
        result = p.stdout.read()
        error  = p.stderr.read()

    except Exception, e:
        zeus.message_box(0, "Unexpected Error: " +  str(e) + "\n\nCommand: " + command + "\n\nMake sure dscanner.exe is in the PATH.", "Dscanner")
        result = ""

    #zeus.message_box(0, "Command: " +  str(command) + "\n\nResult: " + str(result) + "\n\nError: " + str(error))

    return error, result

#
# Style Check: dscanner.exe --styleCheck sourcefile.d
#
# The "--styleCheck" or "-S" option runs some basic static analysis checks
# against the given source files.
#
def styleCheck(file_name):
    # file details
    file_name, file_name_err = outputFileName(file_name)

    # command to write details to error output file
    command = "dscanner.exe --styleCheck " + file_name + " > " + file_name_err + " 2>&1 "

    have_errors = runCommand(command, file_name_err)

    # check for errors
    if have_errors:
        # display the errors in a tool window
        zeus.file_open_tool(file_name_err, "Dscanner Style Check")
    else:
        zeus.message("Style check found no errors.")

#
# Syntax Check
#
# The "--syntaxCheck" or "-s" option prints a listing of any errors or
# warnings found while lexing or parsing the given source file.
#
def syntaxCheck(file_name):
   # file details
   file_name, file_name_err = outputFileName(file_name)

   # command to write details to error output file
   command = "dscanner.exe --syntaxCheck " + file_name + " > " + file_name_err + " 2>&1 "

   # run the command
   have_errors = runCommand(command, file_name_err)

   # check for errors
   if have_errors:
       # display the errors in a tool window
       zeus.file_open_tool(file_name_err, "Dscanner Syntax Check")
   else:
       zeus.message("Syntax check found no errors.")

def findDeclaration(symbol):
    #
    # Find Declaration
    #
    # The "--declaration" or "-d" options allow you to search for a symbols declaration.
    command = "dscanner.exe --declaration " + symbol

    #zeus.message_box(0, command)

    # run the command
    error, result = runCommandEx(command)

    # see if we had success and a result
    if (len(error) == 0):
        # Output example:
        # .\libdparse\src\std\d\ast.d(371:13)
        match = re.search("(.*)\((.*)\:(.*)\)", result)

        if match:
            # get the details
            file_name   = match.group(1)
            line_number = match.group(2)
            column      = match.group(3)

            # open the file
            zeus.file_open(file_name, "Dscanner Syntax Check")

            # locate the symbol in the file
            zeus.set_line_pos(line_number)
            zeus.set_cursor_pos(column)

            zeus.message("Found symbol: " + symbol)
        else:
            zeus.message("No delcaration found for symbol: " + symbol)
    else:
        zeus.message_box(0, command + "\r\n" + error, "Find Declaration Error")
        zeus.message("Unexpected error running command: " + command)
jussij
Site Admin
Posts: 2650
Joined: Fri Aug 13, 2004 5:10 pm

Post by jussij »

Post Reply