diff --git a/.gitmodules b/.gitmodules index 44f0e987..ae65e0a1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -874,6 +874,9 @@ [submodule "vendor/grammars/language-ruby"] path = vendor/grammars/language-ruby url = https://github.com/atom/language-ruby +[submodule "vendor/grammars/sublime-angelscript"] + path = vendor/grammars/sublime-angelscript + url = https://github.com/wronex/sublime-angelscript [submodule "vendor/grammars/TypeScript-TmLanguage"] path = vendor/grammars/TypeScript-TmLanguage url = https://github.com/Microsoft/TypeScript-TmLanguage diff --git a/grammars.yml b/grammars.yml index 4ab5a334..62a50611 100755 --- a/grammars.yml +++ b/grammars.yml @@ -635,6 +635,8 @@ vendor/grammars/standard-ml.tmbundle: - source.ml vendor/grammars/sublime-MuPAD: - source.mupad +vendor/grammars/sublime-angelscript: +- source.angelscript vendor/grammars/sublime-aspectj: - source.aspectj vendor/grammars/sublime-autoit: diff --git a/lib/linguist/heuristics.rb b/lib/linguist/heuristics.rb index 6311d8d5..441a8dc6 100644 --- a/lib/linguist/heuristics.rb +++ b/lib/linguist/heuristics.rb @@ -73,6 +73,14 @@ module Linguist # Common heuristics ObjectiveCRegex = /^\s*(@(interface|class|protocol|property|end|synchronised|selector|implementation)\b|#import\s+.+\.h[">])/ + disambiguate ".as" do |data| + if /^\s*(package\s+[a-z0-9_\.]+|import\s+[a-zA-Z0-9_\.]+;|class\s+[A-Za-z0-9_]+\s+extends\s+[A-Za-z0-9_]+)/.match(data) + Language["ActionScript"] + else + Language["AngelScript"] + end + end + disambiguate ".asc" do |data| if /^(----[- ]BEGIN|ssh-(rsa|dss)) /.match(data) Language["Public Key"] diff --git a/lib/linguist/languages.yml b/lib/linguist/languages.yml index 9f413acc..420fe576 100755 --- a/lib/linguist/languages.yml +++ b/lib/linguist/languages.yml @@ -210,6 +210,17 @@ Alpine Abuild: codemirror_mode: shell codemirror_mime_type: text/x-sh language_id: 14 +AngelScript: + type: programming + color: "#C7D7DC" + extensions: + - ".as" + - ".angelscript" + tm_scope: source.angelscript + ace_mode: text + codemirror_mode: clike + codemirror_mime_type: text/x-c++src + language_id: 389477596 Ant Build System: type: data tm_scope: text.xml.ant diff --git a/samples/ActionScript/FooBar.as b/samples/ActionScript/FooBar.as new file mode 100644 index 00000000..9c833e37 --- /dev/null +++ b/samples/ActionScript/FooBar.as @@ -0,0 +1,35 @@ +// A sample for Actionscript. + +package foobar +{ + import flash.display.MovieClip; + + class Bar + { + public function getNumber():Number + { + return 10; + } + } + + class Foo extends Bar + { + private var ourNumber:Number = 25; + + override public function getNumber():Number + { + return ourNumber; + } + } + + class Main extends MovieClip + { + public function Main() + { + var x:Bar = new Bar(); + var y:Foo = new Foo(); + trace(x.getNumber()); + trace(y.getNumber()); + } + } +} diff --git a/samples/ActionScript/HelloWorld.as b/samples/ActionScript/HelloWorld.as new file mode 100644 index 00000000..0586fd0d --- /dev/null +++ b/samples/ActionScript/HelloWorld.as @@ -0,0 +1,13 @@ +package mypackage +{ + public class Hello + { + /* Let's say hello! + * This is just a test script for Linguist's Actionscript detection. + */ + public function sayHello():void + { + trace("Hello, world"); + } + } +} diff --git a/samples/AngelScript/botmanager.as b/samples/AngelScript/botmanager.as new file mode 100644 index 00000000..b363c153 --- /dev/null +++ b/samples/AngelScript/botmanager.as @@ -0,0 +1,77 @@ +/* +* This is a sample script. +*/ + +#include "BotManagerInterface.acs" + +BotManager::BotManager g_BotManager( @CreateDumbBot ); + +CConCommand@ m_pAddBot; + +void PluginInit() +{ + g_BotManager.PluginInit(); + + @m_pAddBot = @CConCommand( "addbot", "Adds a new bot with the given name", @AddBotCallback ); +} + +void AddBotCallback( const CCommand@ args ) +{ + if( args.ArgC() < 2 ) + { + g_Game.AlertMessage( at_console, "Usage: addbot " ); + return; + } + + BotManager::BaseBot@ pBot = g_BotManager.CreateBot( args[ 1 ] ); + + if( pBot !is null ) + { + g_Game.AlertMessage( at_console, "Created bot " + args[ 1 ] + "\n" ); + } + else + { + g_Game.AlertMessage( at_console, "Could not create bot\n" ); + } +} + +final class DumbBot : BotManager::BaseBot +{ + DumbBot( CBasePlayer@ pPlayer ) + { + super( pPlayer ); + } + + void Think() + { + BotManager::BaseBot::Think(); + + // If the bot is dead and can be respawned, send a button press + if( Player.pev.deadflag >= DEAD_RESPAWNABLE ) + { + Player.pev.button |= IN_ATTACK; + } + else + Player.pev.button &= ~IN_ATTACK; + + KeyValueBuffer@ pInfoBuffer = g_EngineFuncs.GetInfoKeyBuffer( Player.edict() ); + + pInfoBuffer.SetValue( "topcolor", Math.RandomLong( 0, 255 ) ); + pInfoBuffer.SetValue( "bottomcolor", Math.RandomLong( 0, 255 ) ); + + if( Math.RandomLong( 0, 100 ) > 10 ) + Player.pev.button |= IN_ATTACK; + else + Player.pev.button &= ~IN_ATTACK; + + for( uint uiIndex = 0; uiIndex < 3; ++uiIndex ) + { + m_vecVelocity[ uiIndex ] = Math.RandomLong( -50, 50 ); + } + } +} + +BotManager::BaseBot@ CreateDumbBot( CBasePlayer@ pPlayer ) +{ + return @DumbBot( pPlayer ); +} diff --git a/samples/AngelScript/payload.as b/samples/AngelScript/payload.as new file mode 100644 index 00000000..ff6d915c --- /dev/null +++ b/samples/AngelScript/payload.as @@ -0,0 +1,396 @@ +// Sample script. +// Source: https://github.com/codecat/ssbd-payload + +array g_payloadBeginTriggers; +array g_teamForceFields; + +[GameMode] +class Payload : TeamVersusGameMode +{ + [Editable] + UnitFeed PayloadUnit; + + [Editable] + UnitFeed FirstNode; + + [Editable default=10] + int PrepareTime; + + [Editable default=300] + int TimeLimit; + + [Editable default=90] + int TimeAddCheckpoint; + + [Editable default=2] + float TimeOvertime; + + [Editable default=1000] + int TimePayloadHeal; + + [Editable default=1] + int PayloadHeal; + + PayloadBehavior@ m_payload; + + int m_tmStarting; + int m_tmStarted; + int m_tmLimitCustom; + int m_tmOvertime; + int m_tmInOvertime; + + PayloadHUD@ m_payloadHUD; + PayloadClassSwitchWindow@ m_switchClass; + + array@ m_switchedSidesData; + + Payload(Scene@ scene) + { + super(scene); + + m_tmRespawnCountdown = 5000; + + @m_payloadHUD = PayloadHUD(m_guiBuilder); + @m_switchTeam = PayloadTeamSwitchWindow(m_guiBuilder); + @m_switchClass = PayloadClassSwitchWindow(m_guiBuilder); + } + + void UpdateFrame(int ms, GameInput& gameInput, MenuInput& menuInput) override + { + TeamVersusGameMode::UpdateFrame(ms, gameInput, menuInput); + + m_payloadHUD.Update(ms); + + if (Network::IsServer()) + { + uint64 tmNow = CurrPlaytimeLevel(); + + if (m_tmStarting == 0) + { + if (GetPlayersInTeam(0) > 0 && GetPlayersInTeam(1) > 0) + { + m_tmStarting = tmNow; + (Network::Message("GameStarting") << m_tmStarting).SendToAll(); + } + } + + if (m_tmStarting > 0 && m_tmStarted == 0 && tmNow - m_tmStarting > PrepareTime * 1000) + { + m_tmStarted = tmNow; + (Network::Message("GameStarted") << m_tmStarted).SendToAll(); + + for (uint i = 0; i < g_payloadBeginTriggers.length(); i++) + { + WorldScript@ ws = WorldScript::GetWorldScript(g_scene, g_payloadBeginTriggers[i]); + ws.Execute(); + } + } + } + + if (!m_ended && m_tmStarted > 0) + CheckTimeReached(ms); + } + + string NameForTeam(int index) override + { + if (index == 0) + return "Defenders"; + else if (index == 1) + return "Attackers"; + + return "Unknown"; + } + + void CheckTimeReached(int dt) + { + // Check if time limit is not reached yet + if (m_tmLimitCustom - (CurrPlaytimeLevel() - m_tmStarted) > 0) + { + // Don't need to continue checking + m_tmOvertime = 0; + m_tmInOvertime = 0; + return; + } + + // Count how long we're in overtime for later time limit fixing when we reach a checkpoint + if (m_tmOvertime > 0) + m_tmInOvertime += dt; + + // Check if there are any attackers still inside + if (m_payload.AttackersInside() > 0) + { + // We have overtime + m_tmOvertime = int(TimeOvertime * 1000); + return; + } + + // If we have overtime + if (m_tmOvertime > 0) + { + // Decrease timer + m_tmOvertime -= dt; + if (m_tmOvertime <= 0) + { + // Overtime countdown reached, time limit reached + TimeReached(); + } + } + else + { + // No overtime, so time limit is reached + TimeReached(); + } + } + + void TimeReached() + { + if (!Network::IsServer()) + return; + + (Network::Message("TimeReached")).SendToAll(); + SetWinner(false); + } + + bool ShouldFreezeControls() override + { + return m_switchClass.m_visible + || TeamVersusGameMode::ShouldFreezeControls(); + } + + bool ShouldDisplayCursor() override + { + return m_switchClass.m_visible + || TeamVersusGameMode::ShouldDisplayCursor(); + } + + bool CanSwitchTeams() override + { + return m_tmStarted == 0; + } + + PlayerRecord@ CreatePlayerRecord() override + { + return PayloadPlayerRecord(); + } + + int GetPlayerClassCount(PlayerClass playerClass, TeamVersusScore@ team) + { + if (team is null) + return 0; + + int ret = 0; + for (uint i = 0; i < team.m_players.length(); i++) + { + if (team.m_players[i].peer == 255) + continue; + auto record = cast(team.m_players[i]); + if (record.playerClass == playerClass) + ret++; + } + return ret; + } + + void PlayerClassesUpdated() + { + m_switchClass.PlayerClassesUpdated(); + } + + void SetWinner(bool attackers) + { + if (attackers) + print("Attackers win!"); + else + print("Defenders win!"); + + m_payloadHUD.Winner(attackers); + EndMatch(); + } + + void DisplayPlayerName(int idt, SpriteBatch& sb, PlayerRecord@ record, PlayerHusk@ plr, vec2 pos) override + { + TeamVersusGameMode::DisplayPlayerName(idt, sb, record, plr, pos); + + m_payloadHUD.DisplayPlayerName(idt, sb, cast(record), plr, pos); + } + + void RenderFrame(int idt, SpriteBatch& sb) override + { + Player@ player = GetLocalPlayer(); + if (player !is null) + { + PlayerHealgun@ healgun = cast(player.m_currWeapon); + if (healgun !is null) + healgun.RenderMarkers(idt, sb); + } + + TeamVersusGameMode::RenderFrame(idt, sb); + } + + void RenderWidgets(PlayerRecord@ player, int idt, SpriteBatch& sb) override + { + m_payloadHUD.Draw(sb, idt); + + TeamVersusGameMode::RenderWidgets(player, idt, sb); + + m_switchClass.Draw(sb, idt); + } + + void GoNextMap() override + { + if (m_switchedSidesData !is null) + { + TeamVersusGameMode::GoNextMap(); + return; + } + + ChangeLevel(GetCurrentLevelFilename()); + } + + void SpawnPlayers() override + { + if (m_switchedSidesData is null) + { + TeamVersusGameMode::SpawnPlayers(); + return; + } + + if (Network::IsServer()) + { + for (uint i = 0; i < m_switchedSidesData.length(); i += 2) + { + uint peer = uint(m_switchedSidesData[i].GetInteger()); + uint team = uint(m_switchedSidesData[i + 1].GetInteger()); + + TeamVersusScore@ joinScore = FindTeamScore(team); + if (joinScore is m_teamScores[0]) + @joinScore = m_teamScores[1]; + else + @joinScore = m_teamScores[0]; + + for (uint j = 0; j < g_players.length(); j++) + { + if (g_players[j].peer != peer) + continue; + SpawnPlayer(j, vec2(), 0, joinScore.m_team); + break; + } + } + } + } + + void Save(SValueBuilder& builder) override + { + if (m_switchedSidesData is null) + { + builder.PushArray("teams"); + for (uint i = 0; i < g_players.length(); i++) + { + if (g_players[i].peer == 255) + continue; + builder.PushInteger(g_players[i].peer); + builder.PushInteger(g_players[i].team); + } + builder.PopArray(); + } + + TeamVersusGameMode::Save(builder); + } + + void Start(uint8 peer, SValue@ save, StartMode sMode) override + { + if (save !is null) + @m_switchedSidesData = GetParamArray(UnitPtr(), save, "teams", false); + + TeamVersusGameMode::Start(peer, save, sMode); + + m_tmLimit = 0; // infinite time limit as far as VersusGameMode is concerned + m_tmLimitCustom = TimeLimit * 1000; // 5 minutes by default + + @m_payload = cast(PayloadUnit.FetchFirst().GetScriptBehavior()); + + if (m_payload is null) + PrintError("PayloadUnit is not a PayloadBehavior!"); + + UnitPtr unitFirstNode = FirstNode.FetchFirst(); + if (unitFirstNode.IsValid()) + { + auto node = cast(unitFirstNode.GetScriptBehavior()); + if (node !is null) + @m_payload.m_targetNode = node; + else + PrintError("First target node is not a PayloadNode script!"); + } + else + PrintError("First target node was not set!"); + + WorldScript::PayloadNode@ prevNode; + + float totalDistance = 0.0f; + + UnitPtr unitNode = unitFirstNode; + while (unitNode.IsValid()) + { + auto node = cast(unitNode.GetScriptBehavior()); + if (node is null) + break; + + unitNode = node.NextNode.FetchFirst(); + + @node.m_prevNode = prevNode; + @node.m_nextNode = cast(unitNode.GetScriptBehavior()); + + if (prevNode !is null) + totalDistance += dist(prevNode.Position, node.Position); + + @prevNode = node; + } + + float currDistance = 0.0f; + + auto distNode = cast(unitFirstNode.GetScriptBehavior()); + while (distNode !is null) + { + if (distNode.m_prevNode is null) + distNode.m_locationFactor = 0.0f; + else + { + currDistance += dist(distNode.m_prevNode.Position, distNode.Position); + distNode.m_locationFactor = currDistance / totalDistance; + } + + @distNode = distNode.m_nextNode; + } + + m_payloadHUD.AddCheckpoints(); + } + + void SpawnPlayer(int i, vec2 pos = vec2(), int unitId = 0, uint team = 0) override + { + TeamVersusGameMode::SpawnPlayer(i, pos, unitId, team); + + PayloadPlayerRecord@ record = cast(g_players[i]); + record.HandlePlayerClass(); + + if (g_players[i].local) + { + //TODO: This doesn't work well + bool localAttackers = (team == HashString("player_1")); + for (uint j = 0; j < g_teamForceFields.length(); j++) + { + bool hasCollision = (localAttackers != g_teamForceFields[j].Attackers); + + auto units = g_teamForceFields[j].Units.FetchAll(); + for (uint k = 0; k < units.length(); k++) + { + PhysicsBody@ body = units[k].GetPhysicsBody(); + if (body is null) + { + PrintError("PhysicsBody for unit " + units[k].GetDebugName() + "is null"); + continue; + } + body.SetActive(hasCollision); + } + } + } + } +} diff --git a/test/test_grammars.rb b/test/test_grammars.rb index 80550acb..3c140cc7 100644 --- a/test/test_grammars.rb +++ b/test/test_grammars.rb @@ -43,6 +43,7 @@ class TestGrammars < Minitest::Test "82c356d6ecb143a8a20e1658b0d6a2d77ea8126f", # idl.tmbundle "9dafd4e2a79cb13a6793b93877a254bc4d351e74", # sublime-text-ox "8e111741d97ba2e27b3d18a309d426b4a37e604f", # sublime-varnish + "23d2538e33ce62d58abda2c039364b92f64ea6bc", # sublime-angelscript ].freeze # List of allowed SPDX license names diff --git a/test/test_heuristics.rb b/test/test_heuristics.rb index 6414b898..6ea3a958 100644 --- a/test/test_heuristics.rb +++ b/test/test_heuristics.rb @@ -44,6 +44,13 @@ class TestHeuristcs < Minitest::Test assert_equal Language["Objective-C"], match end + def test_as_by_heuristics + assert_heuristics({ + "ActionScript" => all_fixtures("ActionScript", "*.as"), + "AngelScript" => all_fixtures("AngelScript", "*.as") + }) + end + # Candidate languages = ["AGS Script", "AsciiDoc", "Public Key"] def test_asc_by_heuristics assert_heuristics({ diff --git a/vendor/README.md b/vendor/README.md index 8533b99d..52b64c9b 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -15,6 +15,7 @@ This is a list of grammars that Linguist selects to provide syntax highlighting - **Alloy:** [macekond/Alloy.tmbundle](https://github.com/macekond/Alloy.tmbundle) - **Alpine Abuild:** [atom/language-shellscript](https://github.com/atom/language-shellscript) - **AMPL:** [ampl/sublime-ampl](https://github.com/ampl/sublime-ampl) +- **AngelScript:** [wronex/sublime-angelscript](https://github.com/wronex/sublime-angelscript) - **Ant Build System:** [textmate/ant.tmbundle](https://github.com/textmate/ant.tmbundle) - **ANTLR:** [textmate/antlr.tmbundle](https://github.com/textmate/antlr.tmbundle) - **ApacheConf:** [textmate/apache.tmbundle](https://github.com/textmate/apache.tmbundle) diff --git a/vendor/grammars/sublime-angelscript b/vendor/grammars/sublime-angelscript new file mode 160000 index 00000000..a15fe9ee --- /dev/null +++ b/vendor/grammars/sublime-angelscript @@ -0,0 +1 @@ +Subproject commit a15fe9ee571ccd2b7d757c262e0ce5537a349df7 diff --git a/vendor/licenses/grammar/sublime-angelscript.txt b/vendor/licenses/grammar/sublime-angelscript.txt new file mode 100644 index 00000000..afe92b40 --- /dev/null +++ b/vendor/licenses/grammar/sublime-angelscript.txt @@ -0,0 +1,27 @@ +--- +type: grammar +name: sublime-angelscript +license: unlicense +--- +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE.