Code Monkey home page Code Monkey logo

purebasic-language-for-sublime-text's Introduction

PureBasic Language Support for Sublime Text

Syntax Tests

This is a spare time experiment to extend Sublime Text with convenient support for PureBasic.

For now the most recent and stable PureBasic release represents the specification to conform to. Deprecated or removed symbols from past releases can also be supported, if no conflicts result from that.

If you notice anything like a PureBasic keyword or standard library procedure missing, please report an issue.

Syntax Definition

This package ships with a syntax definition which may not cover everything (yet) but most of what matters for a pleasant editing and reading experience. In example: you can quickly navigate to procedure implementations because Sublime Text recognizes them because of the syntax definition.

If you stumble across unrecognized keywords or other expressions, then please file an issue in the issue tracker. As far as the official documentation goes I think I did not miss anything. As #22 shows sometimes the syntax definition needs only a little bit of tweaking to improve support for already known expressions.

Symbol List

Important entities like procedures or structures are listed Sublime Text's symbol list for quick navigation.

Completions

The package ships snippets for regularly used constructs like conditional clauses or procedure implementations. Also it provides completions for keywords and the library procedures (including parameters) shipped with PureBasic.

Build System

This package features a simple Sublime Text build system for PureBasic. It enables you to build and run the current PureBasic source code file.

The build system assumes the PureBasic compiler to be available in the PATH. Please refer to the PureBasic documentation for setting up commandline usage. Not the documentation you likely think of as I would do, too, but the Install.txt shipped with PureBasic. In example for macOS it is located inside the PureBasic.app bundle in Contents/Resources/.

Installation

Package Control

This is available on Package Control.

Manual

Otherwise it can be installed manually by cloning the repository in your package directory of Sublime Text.

History

Years ago, when there was still Sublime Text 2 and no Atom around, I tried to add support for PureBasic to Sublime Text. My main motivation was the cumbersome user experience of the PureBasic IDE, especially on Linux and macOS. Back then I was not close to where I am now in terms of software engineering skills. So I never got far.

When Atom was released it took not much time for me to switch to it as a sidekick instead of Sublime Text. It was much more approachable for me due to the web technologies used and completely free. When I was thinking about a more convenient way of editing PureBasic source code, I created a language support package. It turned out pretty fine (in my opinion). Though PureBasic already was nostalgy instead of a serious interest. Career already took me elsewhere.

So every now and then I have a nostalgic urge to pick up PureBasic again. Though meanwhile I came back to Sublime Text as a sidekick. Atom, rooted in its Electron.js foundation, is a horribly slow and resource hungry editor and at some point I was tired of it. I ported much stuff from my Atom language support package in form of this Sublime Text package. Sublime Text is much faster and efficient, especially with large files. This time I took it further.

Contributing

See CONTRIBUTING.md for further information.

License

This is free and unencumbered software released into the public domain. See LICENSE for further information.

purebasic-language-for-sublime-text's People

Contributors

p4t5h3 avatar tajmone avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

tajmone

purebasic-language-for-sublime-text's Issues

Package Path in Syntax Tests: Use Package Name Instead of Repository Name

Currently, all the syntax tests files have the following opening line:

; SYNTAX TEST "Packages/purebasic-language-for-sublime-text/PureBasic.sublime-syntax"

which means that the tests can only work if the repository was cloned in the Packages folder as is (i.e. with its original repository name).

Since I've cloned the repository forcing its folder name to PureBasic/, in my local working branch I had to tweak the first line of all the test files to:

; SYNTAX TEST "Packages/PureBasic/PureBasic.sublime-syntax"

The problem here is that the actual folder name for the package installed via Package Control is PureBasic/, not the repository name — as configured in Package Control JSON settings.

While right now this might not seem a problem, since syntax tests are export-ignored from the actual package, so only developers and contributors have access to them, if we were to add Python plugins to the package then it would either work for the installed package or its developers — but not for both, since path references would differ!

I strongly suggest that you rename your local clone to PureBasic/, like I did, and then we amend the package path in the test files accordingly, so that we can work with the same environment settings as the final users of the package.

Test files should be valid PureBasic source code

Originally written by @tajmone as part of some contribution guideline proposals:

Because we often need to cover invalid syntax elements which would lead to a compiler error (e.g. an invalid escape like \Z would raise a compiler error), or cover syntax quirks which are valid for the PBCompiler (e.g. a floating point number like 1.575e+1bananas is perfectly valid PB code, just as decimals with leading zero like 0001 are) we should ensure that each test file is a valid PB source that can be compiled.

In the case of tests for invalid code which is expected to raise compiler errors, we should just annotate what compiler errors are expected — e.g. the \Z escape resulting in the compiler error:

Invalid escape sequence in literal string: \Z

For a practical example of the above (except for the file naming convention), see the Tests-Fail/syntax_test_escapes.pb on my fork.

Since syntax tests can quickly grow in number and size, I think that annotating them is beneficial for both maintainers and contributors, especially if in the future the package might need a major revisiting because of a new PureBasic version having been released after the package wasn't updated for a long time (these things do happen, since life is unpredictable and it might as well be that both of use might end up not working with PB in the future, for various reasons).

Leaving behind well annotated tests and code can be a blessing for anyone taking on the project in the future, and also of great help for us if we end up not working on the package for a while — I often end up being spread thin across multiple projects, so my memory tends to fade away fast as I focus on what's right in front of me. Personally, I find annotated comments really useful when resuming work, they allow me to quickly get back on track knowing where I left things at.

Also worth keeping in mind, someone might use this repository as a reference to create a PureBasic syntax for another editor, in which case these comments will help adapting the tests and syntax definition to whatever system/format the target editor adopts. It's not unusual to come across a ST syntax based on a TextMate, Atom or VScode package.

Any annotations in the test file won't end up in the final package, so they are not a burden for the package, but can make a huge difference for contributors.

And, if my add, the list of PureBasic's language quirks is so long that it's hard to remember them all, and without comments they are not intuitive to grasp either. Example, try to compile the following code:

FloatingApples.f  = 1.575e+1apples
FloatingBananas.f = 1.575e+1bananas

If FloatingApples = FloatingBananas
  Debug "They are identical!"
EndIf

it's perfectly valid and will pass the test — a reminder that documentation claims like PureBasic being IEEE 754 compliant need to be taken with a pinch of salt, for even if it is compliant in behavioural terms it surely isn't compliant when it comes to its notation. Since PB relies on .f and .d to distinguish between the different types of float numbers, the PBCompiler captures and ignores any trailing [a-z] characters. Of course, none of this is documented anywhere, it's part of those "PureBasic things" which need to be learned the hard way, via trial and error, and often discovered by accident.

That's an example of an edge case that needs to be well commented in the test files, otherwise anyone looking at the test will fail to grasp what's happening. And of course, being able to compile the actual test file is the final proof regarding the quirk.

Enforce UTF-8 BOM to Prevent Issues with PB IDE

Currently this package will use UTF-8 as the default encoding for PB sources, but that doesn't play well with the standard way sources are handled by the native PB IDE, which uses UTF-8 BOM to distinguish between Unicode and ASCII sources, for historical reasons.

In my previous package I had enforced UTF-8 BOM as the default encoding, for in this case it makes sense, since that's the idiomatic way of the language. Unless we enforce the BOM, we'll be facing encoding conflicts when other users edit files using the PB IDE — without a BOM, the sources will be treated as ASCII file, which will lead to unexpected single-char encoding (according to the OS native settings) and sources corruption for any chars above value 127.

There's nothing wrong with using UTF-8 with a BOM, and it's quite common in the Windows world for the same historical reasons as with PB, i.e. apps switching from single-char encodings to Unicode.

If it's OK with you, I'll create a PR with the UTF-8 BOM settings file (which I still have a copy locally) and also tweak all the test files to add the BOM.

Asterisk after equality sign is scoped as arithmetic operator instead of variable name

The asterisk in this example is scoped as source.purebasic meta.function.purebasic keyword.operator.arithmetic.purebasic. The meta.function.purebasic is because of it being part of a procedure implementation, so that is fine. The keyword.operator.arithmetic.purebasic should be replaced by variable.other.purebasic punctuation.definition.variable.purebasic, though.

Rule(Identifier)\*Address = *ProcedureAddress

Automatic Indentation

As far as I can see there currently is no automatic indentation defined. In example when breaking into a new line after a Procedure declaration.

Check which options are available to implement automatic indentation.

Strings with escape sequences are not parsed correctly

This violates the PureBasic syntax because the escape sequence for the double quote is not recognized.

StartIndex = FindString(StringToSearchIn, "\"", StartIndex)

A tile is necessary to enable escape sequences in the string literal.

StartIndex = FindString(StringToSearchIn, ~"\"", StartIndex)

In both cases the escape sequence is not recognized by the Sublime Text syntax definition which considers the escaped double quote as the string end and the following double quote as the beginning of a new string.

See "literal strings" on general syntax rules.

Procedure calls are not scoped correctly

From the Sublime Text documentation about scope naming:

Function names, including the full path, and all parameters should receive the following scope. The name of the function or method should be variable.function, unless the function is scoped with support.function.

meta.function-call

Currently the procedure name in a call is scoped as:

source.purebasic meta.function.purebasic variable.other.purebasic

Also reassess the parenthesis and parameters.

Consider global match rule for type suffixes

Currently I have implemented the recognition of procedure return types as specific captures in the regular expression.

Research whether type definitions can always and globally be recognized by a pattern like \.\w+ because there is contextual difference.

This could simplify the type recognition and avoid redundancy in the syntax definition.

PureUnit support

Ensure the highlighting and completions for PureUnit (the tool for automated tests shipped with PureBasic itself) are there.

Change project license to MIT

Public domain licensing can become a problem involving the jurisdiction of Germany.

Because I am the sole contributor until now this should be fine and quick to change.

Trailing dollar sign of string variable placeholders in completion breaks them

This is invalid and results in the trigger sequence being replaced by an empty string:

"contents": "OpenWindow(${1:#Window}, ${2:x}, ${3:y}, ${4:InnerWidth}, ${5:InnerHeight}, ${6:Title$})$7",

I had to change it to this:

"contents": "OpenWindow(${1:#Window}, ${2:x}, ${3:y}, ${4:InnerWidth}, ${5:InnerHeight}, ${6:Title.s})$0",

The $0 is the final tab stop in Sublime Text.

Storage modifier `Global` breaks `NewList` declaration

This is recognized correctly:

NewList InputFileLines.s()

This is not:

Global NewList InputFileLines.s()

I also noted this with NewMap. Probably access modifiers like Global break every kind of declaration. Go through all of them and write tests.

Test all the things!

Go through the PureBasic documentation and check for tests being written for all keyword constructs.

Create Branch for Failing Syntax Tests

I have a suggestion to handle failing syntax tests, using a strategy that I've employed in the past already.

Since in the main branch all tests need to pass, because of the CI/Workflow validation, it would be a good idea to have a dedicated fail-tests branch to store all syntax tests that fail, in a dedicated folder like Tests-Fail.

Then, I could create all failing tests in the Tests-Fail/ folder, and create a PR on the dedicated fail-tests branch, so that we're able to keep in the repository all problematic tests which need to be addressed, and as the syntax is fixed, we can then move the failing tests from the Tests-Fail/ folder into Tests/, commit them to main and remove them from the fail-tests branch.

I actually have a number of failing tests that I just need to clean up and place somewhere, before starting to work on the fixes — but I've postponed this work until the syntax includes the version: 2 key, to be sure all tests are ST4 engine compatible.

The reason I like this approach is because it allows to keep track of all syntax problems, edge cases that need to be addressed, and at the same time give respite in terms of having to fix them at the syntax level — the failing tests are there in their dedicated branch, and can be addressed whenever one wishes to, and switching between the main and fail-tests branches is very easy, as well as keeping the latter in synch with the former via simple rebase operations (and see if any changes to the syntax might have fixed some failing tests).

Sometimes I also use this system to pin-down new syntax scopes which I didn't have time to implement (e.g. handling sub-scopes like distinguishing between decimal, octal, hex and binary literal numbers, etc.).

Correctly match pointers

This is a tricky one. Currently I do not know how to achieve this. Hence this is not assigned to a milestone yet.

To my current knowledge, expressions involving pointers can only be interpreted correctly by considering previous declarations. This semantic analysis goes beyond the capabilities of a syntax definition.

Or maybe there can be formulated a set of rules which cover all cases in which pointers can reliably matched.

Asterisks of pointers can be distinguished from the multiplication operator, if the preceding pattern is not a numeric literal or series of word characters. Right? 🤔

Migrating Package to ST4

I strongly advocate for making this an explicit Sublime Text 4 package.

Although right now the syntax doesn't use any of the new ST4 features, the completions are already exploiting new features like completion metadata, which are not supported by ST3 (although they don't break backward compatibility).

My point is that as ST4 features will evolve (which they do, and rather fast), it will become increasingly harder to ensure that the package will still work as expected for, even for ST3 — in theory, new features are simply ignored; in practice it might not be so. Furthermore, why limit the package potential only to ensure backward compatibility with an old and unmaintained version of the editor?

In my local fork, I've already enabled the new ST4 syntax engine by adding version: 2 to the syntax. All tests work fine, which means that currently the syntax doesn't contain any contexts leading to behavioural differences.

But the question of ST3 vs ST4 also affects the API Environments, i.e. which Python version to use within the package — it's either Python 3.3.6 or 3.8, you can't use both. This is probably the crucial point in deciding whether to stick with backward compatibility for ST3 or move along with ST4.

Since this package is currently registered on Package Control as a ST3 package, the decision on whether to switch to ST4 should rather be handled sooner than later, since the switch would come at the detriment of ST3 users who would end up with a package they can no longer update.

In any case, switching from ST3 to ST4 requires some further repository and package interventions:

  1. The CI build job needs to use the new ST4 syntax test suite.
  2. The package version requirements need to be updated on the package control channel.

Amending Package Requirements

Currently this package is registered as working with ST >3092 in the p.json file:

        {
            "name": "PureBasic",
            "details": "https://github.com/peterthomashorn/purebasic-language-for-sublime-text",
            "labels": ["language syntax"],
            "releases": [
                {
                    "sublime_text": ">3092",
                    "tags": true
                }
            ]
        },

so I guess that would have to be amended to >4109, to prevent ST3 users from installing it or further updating it.

NOTE — This pacakge registration system seems rather tedious and inefficient, especially since PRs take weeks to be reviewed. In theory, the version info should be a precise value in order to prevent updating a package beyond the supported version by the user, in practice once a package is submitted that info never gets updated, regardless of the new features used by the package later on.

CI-Testing ST4 Syntaxes

As for the the ST4 syntax tests API, I only had a brief peek at it and, IRC, it's a Python package and no longer a binary executable, as it used to be with ST3. So its setup might require some documentation reading. But I'm not entirely sure about this, and the official documentation isn't much helpful either (hard to find any reference).

I remember having stumbled upon a package that used a Python library for validation, but can't recall any links right now.

But also I see that the official ST syntaxes repository uses a downloadable binary file to validate ST4 syntaxes:

https://github.com/sublimehq/Packages/blob/master/.github/workflows/ci.yml#L50

Probably, right now the current Workflow is just fine, since the syntax is not using any of the new ST4 features or bug fixes. So it might be OK to postpone the migration to the new validator tool, for the time being.

Use a double semicolon as a delimiter in the syntax tests

Originally written by @tajmone:

In example make the first line of the test files:

;; SYNTAX TEST "Packages/PureBasic/PureBasic.sublime-syntax"

This allows to freely place comments in the test files without raising false positives, since the syntax engine will only test comments starting with a double ;;. Without this trick, any comment containing a ^ or <- will trigger a test on the first non-comment line preceding it.

Syntax Tests Guidelines Proposal

I have a few suggestions for improving the syntax tests, based on my past experience with both PB and ST packages.

  • Better Naming Convention — Title-casing the file name after syntax_test_ will improve readability and quick discovery of the various tests:

    • syntax_test_Arithmetic_Operators.pb

    Furthermore, mimicking the scope names will result in related tests grouping together, i.e. instead of syntax_test_arithmetic_operators, etc., make the shared name-part lead the "specialized" part:

    • syntax_test_Operators_Arithmetic.pb
    • syntax_test_Operators_Bitwise.pb
    • syntax_test_Operators_Comparison.pb
  • Document Tested Feautres — IMO we should add useful comments explaining what the various tests are doing, and document how the specific syntax elements work in PureBasic, providing links to the online documentation.

    This is not only helpful for third parties who wish to join the project and contribute, but it's also useful for use, in case the language changes and we need to adapt the syntax accordingly and quickly access the official docs.

  • #49

      ;; SYNTAX TEST "Packages/PureBasic/PureBasic.sublime-syntax"
    

    This allows to freely place comments in the test files without raising false positives, since the syntax engine will only test comments starting with a double ;;. Without this trick, any comment containing a ^ or <- will trigger a test on the first non-comment line preceding it.

  • #50

    In the case of tests for invalid code which is expected to raise compiler errors, we should just annotate what compiler errors are expected — e.g. the \Z escape resulting in the compiler error:

      Invalid escape sequence in literal string: \Z
    

For a practical example of the above (except for the file naming convention), see the Tests-Fail/syntax_test_escapes.pb on my fork.

Since syntax tests can quickly grow in number and size, I think that annotating them is beneficial for both maintainers and contributors, especially if in the future the package might need a major revisiting because of a new PureBasic version having been released after the package wasn't updated for a long time (these things do happen, since life is unpredictable and it might as well be that both of use might end up not working with PB in the future, for various reasons).

Leaving behind well annotated tests and code can be a blessing for anyone taking on the project in the future, and also of great help for us if we end up not working on the package for a while — I often end up being spread thin across multiple projects, so my memory tends to fade away fast as I focus on what's right in front of me. Personally, I find annotated comments really useful when resuming work, they allow me to quickly get back on track knowing where I left things at.

Also worth keeping in mind, someone might use this repository as a reference to create a PureBasic syntax for another editor, in which case these comments will help adapting the tests and syntax definition to whatever system/format the target editor adopts. It's not unusual to come across a ST syntax based on a TextMate, Atom or VScode package.

Any annotations in the test file won't end up in the final package, so they are not a burden for the package, but can make a huge difference for contributors.

And, if my add, the list of PureBasic's language quirks is so long that it's hard to remember them all, and without comments they are not intuitive to grasp either. Example, try to compile the following code:

FloatingApples.f  = 1.575e+1apples
FloatingBananas.f = 1.575e+1bananas

If FloatingApples = FloatingBananas
  Debug "They are identical!"
EndIf

it's perfectly valid and will pass the test — a reminder that documentation claims like PureBasic being IEEE 754 compliant need to be taken with a pinch of salt, for even if it is compliant in behavioural terms it surely isn't compliant when it comes to its notation. Since PB relies on .f and .d to distinguish between the different types of float numbers, the PBCompiler captures and ignores any trailing [a-z] characters. Of course, none of this is documented anywhere, it's part of those "PureBasic things" which need to be learned the hard way, via trial and error, and often discovered by accident.

That's an example of an edge case that needs to be well commented in the test files, otherwise anyone looking at the test will fail to grasp what's happening. And of course, being able to compile the actual test file is the final proof regarding the quirk.

P.S. — Proposals like this one ought to be in Discussions rather than Issues, to keep Issues uncluttered from general talk and focus on implementation tasks. You might consider enabling Discussions in the repository.

List declaration parsed wrong

NewList InputFileLines.s() is parsed wrong. The type suffix is recognized as a procedure call. The list name is not recognized at all.

Unify indentation

Provide a language specific default setting for indentation and also check all snippets to have the same indentation.

Verify correct indentation in snippets

We have defined an indentation of two spaces in our .editorconfig which diverges from the indentation in the shipped completions. This should be consistent.

List all declarations in the symbol list

As I found out through the community documentation, some scopes have to be explicitly enabled for listing in the symbol list of Sublime Text.

Check whether this is up to date or if YAML files can also be used. Then add all sorts of declarations to the symbol list. In example Structure definitions are not listed yet.

Support structure definitions

Currently only the basic Structure, EndStructure and Extends keywords are recognized. The types themselves are not recognized as type declarations / entity implementations.

Further its field definitions are not parsed as well as StructureUnion.

Comparison Operators: Wrong Scopes and Broken Ligatures

There are multiple problem with the state of how comparison operators are captured (refer to the syntax_test_operators_comparison.pb on my fork), both in term of wrong scopes and breaking ligatures support:

  • Wrong Scope — they are captured as keyword.comparison instead of keyword.operator.comparison. Instead of the obsolete (and incomplete, opinionated) documentation on Scope Naming, see how the native syntaxes actually capture these operators in other languages, as well as good packages do. Other useful references:

  • Composite Operators Split Tokens — most of the bi-character operators are not being captured as a single token, but as two separate tokens, which prevents showing correct fonts ligatures.

    This needs to be manually verified by visual inspection, since the syntax tester can't distinguish between two separate adjacent tokens with a same scope and a single-captured token. To check this, past the following code in a PB file in ST:

    If x >= y ; Expected ligature: >=
    
    If x => y ; Expected ligature: =>
    
    If x <= y ; Expected ligature: <=
    
    If x <> y ; Expected ligature: <>
    

    You should notice the difference between the actual operator and the way it's represented in the comment next to, since the comment should use the font ligatures instead, because it's not splitting the tokens.

    If you're not seeing the ligatures properly, ensure that are using a good code font that supports ligatures (try FiraCode, one of the best for coder), and that you have correctly enabled ligatures in ST settings (globally or for the PB syntax specifically). Ligatures setting might depend on the OS being used, by usually the default settings should work fine, supporting ligatures out of the box (it wasn't so in ST3 though). Depending on the specific language, and which ligatures it supports, you might have to tweak the ligature settings to restrict the applied groups (clig, liga and calt).

    But right now, the problem is that ligatures are showing correctly in the comments but not in the operators themselves, due to split-capturing which doesn't preserve them as a single token.

  • Assignment = in Composite Operators — The = in composite operators is always captured as keyword.operator.assignment. When the capturing RegEx is fixed, and composite operators are captured in the correct way and with the right matching precedence, this should be automatically solved.

  • Assignment vs Comparison — The = operator is currently being always captured as keyword.operator.assignment, even in contexts where it's actually used for comparison.

    This one is going to be trickier to solve, since it depends on context and not the capturing RegEx. We'll need to be able to tweak the syntax to become context-aware so that it can capture it as a keyword.operator.comparison in the right contexts.

    But this is going to require strong tests coverage, to ensure we don't break the syntax. Also, since ST4 introduces better debugging info when inspecting scopes, we should substitute anonymous contexts with named contexts, so we can better trace the path leading to the various scoping result.

    Unfortunately PureBasic doesn't use == as a comparison operator, like most modern languages do, instead it uses = for both assignment and comparison — a bad design choice, and one which we'll have to cope with, since it doesn't look like the new PB6 is fixing this.


I should be able to fix all the scoping problems described above, and ensure that composite operators are captured as a single token. The RegEx from my old Sublime PureBasic package should work fine, but I can also reuse various RegExs which I wrote for syntax highlighters, and I have all the required test files lying around in my hard disk. Just give me some time and I should come up with a PR that fixes these.

As for the "Assignment vs Comparison" problem with =, I will create a dedicated branch to fix it, since it's probably going to require a multi-step approach, and requires extensive testing to make sure that all the correct comparison contexts are covered, without breaking anything. I'll update you when I have set-up a dev branch for this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.